react 常用钩子 hooks 总结
文章目录
- React钩子概念图
- 状态管理钩子 state management
- 副作用钩子 effect hooks
- 引用钩子 Ref hooks
- 上下文钩子
- 其他钩子
- 过渡钩子 处理过渡效果
- 性能优化钩子 performance hooks
- React 19 新钩子
React钩子概念图
状态管理钩子 state management
useState useReducer useSyncExternalStore
useState 最常用钩子
const [value, setValue] = useState('')
const handleChange = (e) => {setValue(e.target.value)
}
<input type="text" value = {value} onChange = {handleChange}></input>
#捕获用户输入值的变化
import React, { useState } from 'react';function ToggleComponent() {const [isVisible, setIsVisible] = useState(false);return (<><button onClick={() => setIsVisible(!isVisible)}>Toggle</button>{isVisible && <div>Content to Show/Hide</div>}</>);
}export default ToggleComponent;
# 控制显隐
useReducer 管理更为复杂状态的钩子
在一个reducer函数内管理状态
import React, { useReducer } from 'react';// Reducer 函数
const reducer = (state, action) => {switch (action) {case 'increment':return state + 1;default:return state; // 默认返回当前状态}
};function Counter() {const [count, dispatch] = useReducer(reducer, 0);return (<><p>Count: {count}</p><button onClick={() => dispatch('increment')}>Increment</button></>);
}export default Counter;
这个其实有点类似 Vue 的 vuex 两者设计思想类似 学过这两种语言的应该会有感受
相似点
- 状态定义:
- 在
useReducer
中,初始状态通过useReducer(reducer, initialState)
定义。 - 在 Vuex 中,初始状态通过
state
属性定义。
- 在
- 状态更新逻辑:
- 在
useReducer
中,reducer
函数负责根据action
计算新的状态。 - 在 Vuex 中,
mutations
是唯一可以修改状态的地方,类似于reducer
。
- 在
- 触发状态更新:
- 在
useReducer
中,通过dispatch(action)
触发状态更新。 - 在 Vuex 中,通过
commit('mutation')
或dispatch('action')
触发状态更新。
- 在
- 集中化管理:
- 两者都提供了一种集中化的方式管理状态,避免了组件内部直接操作状态的混乱。
代码对比
React useReducer
import React, { useReducer } from 'react';const initialState = 0;// Reducer 函数
const reducer = (state, action) => {switch (action.type) {case 'increment':return state + 1;case 'decrement':return state - 1;default:return state;}
};function Counter() {const [count, dispatch] = useReducer(reducer, initialState);return (<><p>Count: {count}</p><button onClick={() => dispatch({ type: 'increment' })}>Increment</button><button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button></>);
}export default Counter;
Vuex Store
// Vuex store
const store = new Vuex.Store({state: {count: 0,},mutations: {increment(state) {state.count++;},decrement(state) {state.count--;},},actions: {increment({ commit }) {commit('increment');},decrement({ commit }) {commit('decrement');},},
});// Vue 组件
<template><div><p>Count: {{ count }}</p><button @click="increment">Increment</button><button @click="decrement">Decrement</button></div>
</template><script>
export default {computed: {count() {return this.$store.state.count;},},methods: {increment() {this.$store.dispatch('increment');},decrement() {this.$store.dispatch('decrement');},},
};
</script>
主要区别
特性 | React useReducer | Vuex |
---|---|---|
作用范围 | 适用于单个组件或父子组件间的状态管理 | 适用于全局状态管理(跨多个组件) |
触发方式 | dispatch(action) | commit('mutation') 或 dispatch('action') |
是否支持异步操作 | 不直接支持,需要结合其他工具(如 useEffect ) | 支持异步操作(通过 actions ) |
模块化 | 需要手动组织代码结构 | 提供模块化支持(modules ) |
学习曲线 | 较低,适合小型到中型应用 | 较高,适合中大型应用 |
import React, { useReducer } from 'react';// 初始状态
const initialState = { email: '', password: '' };//初始值由 initialState 定义:{ email: '', password: '' }。// Reducer 函数
// 它接收一个 action 对象(通常包含 type 和可选的 payload),并将这个 action 传递给 reducer。
// reducer 根据 action.type 和 action.payload 计算并返回新的状态。
const reducer = (state, action) => {switch (action.type) {case 'SET_EMAIL':return {...state,email: action.payload,};case 'SET_PASS':return {...state,password: action.payload,};default:return state;}
};function LoginForm() {const [state, dispatch] = useReducer(reducer, initialState); // state 是当前的状态对象,在这里它是一个包含 email 和 password 的对象。//每次调用 dispatch 时,state 会根据 reducer函数 的逻辑更新为新的值。return (<form>{/* 绑定 email 输入框 */}<inputtype="email"value={state.email}onChange={(e) => {dispatch({ type: 'SET_EMAIL', payload: e.target.value });}}placeholder="Enter email"/>{/* 绑定 password 输入框 */}<inputtype="password"value={state.password}onChange={(e) => {dispatch({ type: 'SET_PASS', payload: e.target.value });}}placeholder="Enter password"/>{/* 显示当前状态 */}<p>Email: {state.email}</p><p>Password: {state.password}</p></form>);
}export default LoginForm;
useSyncExternalStore 很少用 将非react状态钩子存储到react中
副作用钩子 effect hooks
useEffect useLayoutEffect useInsertionEffect
const [count, setCount] = useState(0)useEffect(() => {document.title = `You clicked ${count} times`
},[count])
//给 useEffect 提供一个运行函数 传入依赖项数组 依赖项数组变化时函数运行<button onClick={() => setCount(count + 1)}>Click Me</button>
副作用函数一般有两种 比如事件驱动副作用 点击按钮等事件 第二种 渲染驱动副作用 页面渲染后运行 调取接口
const ref = useRef(null)useEffect(() => {if(isPlaying){ref.current.play()}else{ref.current.pause()}
},[isPlaying])<video ref={ref} src={src} loop playsInline />
useLayoutEffect react 绘制后运行
useEffect 异步执行 钩子不会阻塞浏览器绘制(paint),这意味着它会在浏览器完成渲染后异步执行。这是最常用的副
作用钩子,适用于大多数情况,比如数据获取、订阅或者手动DOM更新等。
useLayoutEffect 会在所有的 DOM 变更之后同步执行。这可以保证在任何浏览器绘制之前,所有的 DOM 操作已经完成。因此,如果
你的副作用涉及到 DOM 操作,并且这些操作会影响到页面的布局或视觉效果,你应该使用 useLayoutEffect
来避免闪烁或其他视觉问
题。
const ref = useRef(null)
const [tooltipHeight, setTooltipHeight] = useState(0)useLayoutEffect(() => {const {height} = ref.current.getBoundingClientRect()setTooltipHeight(height)
},[])
useInsertionEffect 在渲染过程中插入样式 它可以让你在组件渲染之前插入样式定义,从而避免样式闪烁或未定义样式的问题。很少使
用
引用钩子 Ref hooks
useRef useImperativeHandle
useRef 访问dom实例 ref.current
const [timer, setTimer] = useState(0);
const intervalRef = useRef();
//启动计时器函数
const startTimer = () => {intervalRef.current = setInterval(() => {setTimer((prevTimer) => prevTimer + 1);}, 1000);
};
//startTimer 函数通过 setInterval 每隔 1 秒调用一次回调函数,该回调函数将 timer 状态增加 1。
//intervalRef.current 存储了 setInterval 返回的定时器 ID,以便稍后可以停止计时器。
// 停止计时器
const stopTimer = () => {clearInterval(intervalRef.current);
};<p>Timer: {timer} seconds</p>
<button onClick={startTimer}>Start Timer</button>
<button onClick={stopTimer}>Stop Timer</button>
useImperativeHandle 也是一种引用钩子 很少使用 当你需要在父组件中操作子组件的某些功能,但又不希望暴露整个子组件实例时。
父组件获取子组件输入框的焦点
父组件
import React, { useRef } from 'react';
import ChildComponent from './ChildComponent';function ParentComponent() {const childRef = useRef();const handleFocus = () => {childRef.current.focusInput();};const handleClear = () => {childRef.current.clearInput();};return (<div><ChildComponent ref={childRef} /><button onClick={handleFocus}>Focus Input</button><button onClick={handleClear}>Clear Input</button></div>);
}export default ParentComponent;
子组件
import React, { useRef, useImperativeHandle, forwardRef } from 'react';const ChildComponent = forwardRef((props, ref) => {const inputRef = useRef();// 自定义暴露的方法useImperativeHandle(ref, () => ({focusInput: () => {inputRef.current.focus();},clearInput: () => {inputRef.current.value = '';},}));return <input type="text" ref={inputRef} placeholder="Enter text" />;
});export default ChildComponent;
useImperativeHandle 定义了两个方法:
-
focusInput
:让输入框获得焦点。 -
clearInput
:清空输入框的内容。 -
父组件只能调用这两个方法,而不能直接访问
inputRef
或其他内容。 -
useImperativeHandle
必须与forwardRef
配合使用,因为只有forwardRef
才能将ref
传递到子组件。
上下文钩子
useContext
读取上下文值
//1 创建context
const AppContext = createContext()
//2 用Provider组件包裹
<AppContext.Provider value="你好"><App></App>
</AppContext.Provider>
//3 useContext 获取值
function Title(){const text = useContext(AppContext)return <h1>{text}</h1>
}
其他钩子
useDebugValue useId
useDebugValue devtool调试用 很少使用
useId 创建唯一id 但不能用作key
function Form() {return (<form><EmailInput name="Email" /><EmailInput name="Confirm Email" /></form>);
}function EmailInput({ name }) {const id = useId();return (<><label htmlFor={id}>{name}</label><input id={id} type="email" /></>);
}
过渡钩子 处理过渡效果
useTransition
useDeferredValue
useTransition 指定状态更新 不紧急
const [filter, setFilter] = useState('');
const [inputValue, setInputValue] = useState('');
const [isPending, startTransition] = useTransition();const filteredItems = items.filter(item => item.includes(filter));<inputvalue={inputValue}onChange={(event) => {setInputValue(event.target.value);startTransition(() => {setFilter(event.target.value);});}}placeholder="Type to filter..."
/>
{isPending ? <p>Loading...</p> : filteredItems.map(item => <div key={item}>{item}</div>)}
useDeferredValue 延迟状态更新
性能优化钩子 performance hooks
useMemo useCallback
useMemo 提升性能 useMemo必须返回一个值
useMemo
是一个 React Hook,用于缓存计算结果,避免在每次渲染时都重新计算。
function SumComponent({ numbers }) {const sum = useMemo(() => {return numbers.reduce((total, n) => total + n, 0);}, [numbers]);return <h1>Sum: {sum}</h1>;
}
//接收一个数字数组 numbers 作为 props,并计算这些数字的总和。通过使用 useMemo,确保了只有在 numbers 发生变化时才重新计算总
//和,从而提高了组件的性能,避免了不必要的重复计算。
import React, { useMemo } from 'react';function SumComponent({ numbers }) {// 使用 useMemo 缓存计算结果const sum = useMemo(() => {console.log('Calculating sum...');return numbers.reduce((total, num) => total + num, 0);}, [numbers]); // 只有当 numbers 发生变化时才重新计算return (<div><h1>Sum: {sum}</h1></div>);
}export default function App() {const [numbers, setNumbers] = React.useState([1, 2, 3, 4, 5]);const [count, setCount] = React.useState(0);// 添加一个随机数到数组中const addRandomNumber = () => {setNumbers([...numbers, Math.floor(Math.random() * 100)]);};return (<div><h2>Current Count: {count}</h2><button onClick={() => setCount(count + 1)}>Increase Count</button><button onClick={addRandomNumber}>Add Random Number</button><SumComponent numbers={numbers} /></div>);
}
useCallback 缓存回调函数
function Counter() {const [count, setCount] = useState(0);const increment = useCallback(() => {setCount((c) => c + 1);}, []);return (<><div>{count}</div><Button onClick={increment} /></>);
}function Button({ onClick }) {return <button onClick={onClick}>Click me</button>;
}
React 19 新钩子
useFormStatus useFormState useOptimistic use