文章目录
React钩子概念图
状态管理钩子 state management
useState useReducer useSyncExternalStore
useState 最常用钩子
react
const [value, setValue] = useState('')
const handleChange = (e) => {
setValue(e.target.value)
}
<input type="text" value = {value} onChange = {handleChange}></input>
#捕获用户输入值的变化
react
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函数内管理状态
react
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
jsx
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
javascript
// 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 ) |
学习曲线 | 较低,适合小型到中型应用 | 较高,适合中大型应用 |
react
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 输入框 */}
<input
type="email"
value={state.email}
onChange={(e) => {
dispatch({ type: 'SET_EMAIL', payload: e.target.value });
}}
placeholder="Enter email"
/>
{/* 绑定 password 输入框 */}
<input
type="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
react
const [count, setCount] = useState(0)
useEffect(() => {
document.title = `You clicked ${count} times`
},[count])
//给 useEffect 提供一个运行函数 传入依赖项数组 依赖项数组变化时函数运行
<button onClick={() => setCount(count + 1)}>Click Me</button>
副作用函数一般有两种 比如事件驱动副作用 点击按钮等事件 第二种 渲染驱动副作用 页面渲染后运行 调取接口
react
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
来避免闪烁或其他视觉问
题。
react
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
react
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 也是一种引用钩子 很少使用 当你需要在父组件中操作子组件的某些功能,但又不希望暴露整个子组件实例时。
父组件获取子组件输入框的焦点
父组件
react
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;
子组件
react
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
读取上下文值
react
//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
react
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 指定状态更新 不紧急
react
const [filter, setFilter] = useState('');
const [inputValue, setInputValue] = useState('');
const [isPending, startTransition] = useTransition();
const filteredItems = items.filter(item => item.includes(filter));
<input
value={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,用于缓存计算结果,避免在每次渲染时都重新计算。
react
function SumComponent({ numbers }) {
const sum = useMemo(() => {
return numbers.reduce((total, n) => total + n, 0);
}, [numbers]);
return <h1>Sum: {sum}</h1>;
}
//接收一个数字数组 numbers 作为 props,并计算这些数字的总和。通过使用 useMemo,确保了只有在 numbers 发生变化时才重新计算总
//和,从而提高了组件的性能,避免了不必要的重复计算。
react
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 缓存回调函数
react
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