状态 Hooks 的初始化与契约
如何避免每次渲染都计算初始状态?
- 惰性初始化: 给 useState 传初始化函数,让 React 在建立状态时调用;传
createInitial()则 JavaScript 在每次执行组件时先调用它; - 初值变化: 后续传入不同 initialState 不会覆盖已存在的状态,重置方式见保留与重置 state;
- 纯度检查: 初始化函数不接收参数,应为纯函数,StrictMode 开发检查可能调用两次并忽略一次结果;
| 写法 | 初始化时 | 普通重新渲染时 |
|---|---|---|
useState(createInitial()) | 计算并采用 | 仍计算,但 React 忽略结果 |
useState(createInitial) | React 调用并采用 | 不因传参再次调用 |
useReducer(reducer, arg, init) | 使用 init(arg) | 保留当前状态 |
function initializeTasks(initialTasks) {
return initialTasks.map((task) => ({ ...task, selected: false }));
}
function Tasks({ initialTasks }) {
const [tasks, dispatch] = useReducer(tasksReducer, initialTasks, initializeTasks);
return <TaskList tasks={tasks} dispatch={dispatch} />;
}
如何把函数本身存进 state?
- 函数歧义: useState 的函数参数被当成 initializer,setter 的函数参数被当成 updater,不能直接用它们保存函数值;
- 包一层返回: 使用
useState(() => fn)初始化,以setFn(() => nextFn)更新,外层函数返回真正要保存的函数;
const [handler, setHandler] = useState(() => initialHandler);
function replaceHandler() {
setHandler(() => nextHandler);
}