react3-useEffect()、组件通信(props、context)、useContext()、useReducer()、useRef()、自定义钩子Custom Hooks、useId hook、useForm
useEffect()
允许执行一些副作用,如在组件中直接获取数据、更新DOM、打印某些内容等
语法:useEffect(() => {})
依赖数组
-
未提供数组(依赖数组),那么回调函数会在每次渲染时被调用
import { useState, useEffect } from "react" const App = () => { const [value, setValue] = useState(0) // 会在每次重新渲染时允许这段代码 useEffect(() => { console.log('useEffect') document.title = `Increment ${value}` }) return <> <h2>{value}</h2> <button onClick={() => setValue(value+1)}>Increment</button> </> } export default App -
当指定一个空依赖数组时,使用useEffect时,只会在useEffect钩子中可用的函数上查找初始渲染,如组件的第一次渲染,而不是当组件更改时(只在初始时渲染一次)
useEffect(() => { console.log('useEffect') document.title = `Increment ${value}` }, []) -
如果数组中提供了某状态的值,每当该状态发生变化该组件将重新渲染并触发回调函数
使用场景
-
若需监听所有更新(如实时计算),可省略依赖数组
-
若只需初始化操作(如数据请求),应使用空数组
-
若需针对性监听某些状态变化,应在数组中指定依赖项
无依赖数组和有依赖项的区别
-
副作用函数会在每次组件渲染后都执行(包括首次渲染和所有后续更新)
-
相当于同时监听了组件的挂载和所有可能的更新
-
可能导致性能问题,因为执行频率过高
条件渲染
不允许在条件渲染内部包裹useEffect钩子
useEffect(以及所有 React Hooks)必须在组件的顶层作用域中调用,不能放在条件语句、循环或嵌套函数内部
原因:可能导致某些渲染中 Hook 被跳过,使得React 无法正确关联 Hook 与对应的状态
import { useState, useEffect } from "react"
const App = () => {
const [data, setData] = useState([])
useEffect(() => {
async function getData() {
const res = await fetch('https://jsonplaceholder.typicode.com/todos')
const dataRes = await res.json() // 一定要用await
if (dataRes && dataRes.length){
setData(dataRes)
}
}
getData()
}, [])
return <>
<ul>
{
data.map((item) => {
return <li key={item.userId}> {item.title} </li>
})
}
</ul>
</>
}
export default App

数据传递
prop drilling(属性钻取)
通过props父子组件通信来传递数据

父组件:
import ComponentA from "./components/ComponentA"
const App = () => {
const name = 'yuanyu'
return <>
<ComponentA name={name}/>
</>
}
export default App
子组件1:
import ComponentB from './ComponentB'
const ComponentA = ({name}) => {
return (
<div>
<ComponentB name={name}/>
</div>
)
}
export default ComponentA
子组件2:
import ComponentC from './ComponentC'
const ComponentB = ({name}) => {
return (
<div>
<ComponentC name={name}/>
</div>
)
}
export default ComponentB
子组件3:
const ComponentC = ({name}) => {
return (
<div>
<h1>{name}</h1>
</div>
)
}
export default ComponentC

context API
允许在组件树中管理共享状态,无需手动在每个级别上传递props
步骤:
-
在父组件中引用createContext:
import { createContext } from "react" -
创建createContext实例:
export const Data = createContext() -
包裹子组件并通过示例.Provider提供数据:
<Data.Provider value={name}> <子组件/> </Data.Provider> -
子组件中通过实例.Consumer+回调函数接收数据:
import { Data } from "../App"
传递一个数据
父组件:
import ComponentA from "./components/ComponentA"
import { createContext } from "react"
export const Data = createContext() // 创建createContext实例
const App = () => {
const name = 'yuanyu'
return <>
<Data.Provider value={name}>
<ComponentA/>
</Data.Provider>
</>
}
export default App
子组件1:
import ComponentB from './ComponentB'
const ComponentA = () => {
return (
<div>
<ComponentB/>
</div>
)
}
export default ComponentA
子组件2:
import ComponentC from './ComponentC'
const ComponentB = () => {
return (
<div>
<ComponentC/>
</div>
)
}
export default ComponentB
子组件3:
import { Data } from "../App"
const ComponentC = () => {
return (
<Data.Consumer>
{
(name) => {
return <h1>{name}</h1>
}
}
</Data.Consumer>
)
}
export default ComponentC
传递两个数据
父组件:
import ComponentA from "./components/ComponentA"
import { createContext } from "react"
export const Data = createContext() // 创建createContext实例
export const Data1 = createContext()
const App = () => {
const name = 'yuanyu'
const age = 24
return <>
<Data.Provider value={name}>
<Data1.Provider value={age}>
<ComponentA />
</Data1.Provider>
</Data.Provider>
</>
}
export default App
孙组件:
import { Data, Data1 } from "../App"
const ComponentC = () => {
return (
<Data.Consumer>
{
(name) => {
return (
<Data1.Consumer>
{
(age) => {
return (
<h1>My name is {name}, my age is {age}</h1>
)
}
}
</Data1.Consumer>
)
}
}
</Data.Consumer>
)
}
export default ComponentC
传递多个数据
将多个属性写入一个对象中进行传递
父组件:
import ComponentA from "./components/ComponentA"
import { createContext } from "react"
export const Data = createContext() // 创建createContext实例
export const Data1 = createContext()
const App = () => {
const name = 'yuanyu'
const age = 24
const user = {
name,
age
}
return <>
<Data.Provider value={user}>
<ComponentA />
</Data.Provider>
</>
}
export default App
孙组件:
import { Data } from "../App"
const ComponentC = () => {
return (
<Data.Consumer>
{
(user) => {
return <h1>
My name is {user.name}, age is {user.age}.
</h1>
}
}
</Data.Consumer>
)
}
export default ComponentC
useContext()
允许在函数中直接访问由context对象提供的context值
步骤:
-
在父组件中通过createContext提供值
-
在子组件中引入useContext()钩子:
import { useContext } from "react" -
获取context的值:
const userName = useContext(Data) -
在子组件中使用
父组件:
import ComponentA from "./components/ComponentA"
import { createContext } from "react"
export const Data = createContext() // 创建createContext实例
export const Data1 = createContext()
const App = () => {
const name = 'yuanyu'
const age = 24
return <>
<Data.Provider value={name}>
<Data1.Provider value={age}>
<ComponentA/>
</Data1.Provider>
</Data.Provider>
</>
}
export default App
孙组件:
import { Data, Data1 } from "../App"
import { useContext } from "react"
const ComponentC = () => {
const userName = useContext(Data)
const userAge = useContext(Data1)
return (
<h1>My name is {userName}, my age is {userAge}</h1>
)
}
export default ComponentC
zustand
redux toolkit
useReducer()
与useState()相似,但是为更复杂的状态对象或状态过渡设计的,涉及多个子值,允许以功能和不变的方法管理
语法:const [state,dispatch] = useReducer(reducer, initialState)
- initialState:初始值
- reducer:一个函数,用于执行某些操作并返回新的状态,接收state、action两个参数
- state:当前状态
- dispatch:调用操作来发送到reducer的函数并更新状态
例1:
import { useReducer } from "react"
const App = () => {
const initialState = {count: 0}
const reducer = (state, action) => {
switch (action.type) {
case 'increment':
return {...state, count: state.count + 1}
case 'decrement':
return {...state, count: state.count - 1}
case 'reset':
return {...state, count: 0}
}
}
const [state,dispatch] = useReducer(reducer, initialState)
return <>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'reset'})}>reset</button>
<h1>count: {state.count}</h1>
</>
}
export default App
例2:
const initialState = {count: 0}
function counterReducer(state, action) {
switch (action.type) {
case 'increment':
return {...state, count: state.count + 1}
case 'decrement':
return {...state, count: state.count - 1}
case 'incrementByAmount':
return {...state, count: state.count + action.amount}
case 'decrementByAmount':
return {...state, count: state.count - action.amount}
default:
return state
}
}
export {initialState, counterReducer}
import {initialState, counterReducer} from './counterReducer'
import { useReducer, useState } from 'react'
const Counter = () => {
const [state, dispatch] = useReducer(counterReducer, initialState)
const [inputValue, setInputValue] = useState(0)
return (
<div>
<h1>count: {state.count}</h1>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<input type="number" value={inputValue} onChange={(e) => setInputValue(e.target.value)} />
<button onClick={() => dispatch({type: 'incrementByAmount', amount: +inputValue})}>Add</button>
<button onClick={() => dispatch({type: 'decrementByAmount', amount: +inputValue})}>Substract</button>
</div>
)
}
export default Counter
import Counter from './components/Counter'
const App = () => {
return <>
<Counter/>
</>
}
export default App

useRef()
访问、与DOM元素交互的方式
import { useRef } from "react"
const App = () => {
const element = useRef(null)
console.log(element) // current: null
return <></>
}
export default App
current属性允许操作DOM或对DOM做一系列操作
例:
import { useRef } from "react"
const App = () => {
const element = useRef(null)
console.log(element) // current: null
const focusInput = () => {
element.current.focus() //通过current属性操作DOM
element.current.value = 'yuanyu'
}
return <>
<input type="text" ref={element}/>
<button onClick={() => focusInput()}>focus & write</button>
</>
}
export default App

Custom Hooks
自定义钩子是JS函数以use开头,可以复用简化代码
例:
import { useState, useEffect } from 'react'
const useFetch = (url) => {
const [data, setData] = useState(null)
useEffect(() => {
fetch(url).then((res) => {
res.json()
}).then((data) => setData(data))
}, [url])
return [data]
}
export default useFetch
useId hook
在react中用于为组件生成唯一的id
例:
import { useId } from "react"
const UniqueId = () => {
const id = useId()
return (
<div>
<label htmlFor={id}>Email</label>
<input type="email" id={id} />
</div>
)
}
export default UniqueId

例:
import { useId } from "react"
const UniqueId = () => {
const id = useId()
return (
<div>
<label htmlFor={`${id}-email`}>Email</label>
<input type="email" id={`${id}-email`} />
<label htmlFor={`${id}-password`}>Password</label>
<input type="password" id={`${id}-password`} />
</div>
)
}
export default UniqueId

useForm
网址 :Get Started
- 安装:
npm i react-hook-form - 使用:
const {register, handleSubmit, formState: {errors, isSubmitting}} = useForm()- register:用于将输入字段连接到表单
- handleSubmit:表单提交处理函数
- errors:表单的验证错误
import { useForm } from 'react-hook-form'
interface FormData {
name: string
email: string
password: string
}
const Form = () => {
const { register, handleSubmit, formState: { errors, isSubmitting } } = useForm<FormData>()
const onSubmit: handleSubmit<FormData> = (data) => {
console.log(data)
}
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div className="name">
<label htmlFor="name">Name:</label>
<input type="text" id='name' {...register("name", { required: 'Name is required.' })} />
{errors.name && <p style={{ color: 'red' }}>{errors.name.message}</p>}
</div>
<div className="email">
<label htmlFor="email">Email:</label>
<input type="email" id='email' {...register("email", {
required: 'Email is required.',
pattern: {
value: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
message: 'Invalid email address.'
}
})} />
{errors.email && <p style={{ color: 'red' }}>{errors.email.message}</p>}
</div>
<div className="password">
<label htmlFor="password">Password:</label>
<input type="password" id='password' {...register("password", { required: 'Password is required.' })} />
{errors.password && <p style={{ color: 'red' }}>{errors.password.message}</p>}
</div>
<button type='submit' disabled={isSubmitting}>Submit</button>
</form>
)
}
export default Form


浙公网安备 33010602011771号