在使用 React 的过程中,很多人会遇到这样一个疑问:
组件明明重新渲染了,为什么 useState 里面的状态却没有回到初始值?
要理解这个现象,我们需要先弄清楚 React 的渲染机制 和 组件状态的存储方式。
渲染和挂载的区别
在 React 中,渲染并不等于重新创建组件实例
- 挂载阶段(Mount):组件第一次出现在页面中。此时 React 会执行函数组件,初始化状态首次进行渲染,并在内存里建立 Fiber 节点。
- 更新阶段(Update):组件因为 props 改变、state 改变或父组件更新而再次执行渲染。此时 React 会重用内部状态更新触发渲染的函数组件(已有的 Fiber 节点),此操作不会重新初始化状态。
- 卸载阶段(Unmount):组件被移除, Fiber 节点销毁,状态才会真正丢失。
React 组件的状态存放在哪里?
React 内部有一个 Hook 链表结构,所有的 useState 状态都挂在 Fiber 节点上
const [count, setCount] = useState(0);
- 第一次挂载时,React 会调用 useState(0),把初始值 0 存在 Fiber 节点的 Hook 中
- 当组件每次重新执行更新状态时,React 会找到对应的 Hook 节点,直接返回之前存储的状态,而不是再次用 0
状态为什么不会刷新?
每个组件实例都有自己独立的状态存储。当组件因 props 改变而重新渲染时,React 没有销毁旧的 Fiber 节点。此时如果没有配置 key 或者组件的 key 不变,组件并没有被卸载,它就是同一个组件实例,组件内的状态就会被保留并展现在页面效果
function Counter({ initialValue }) {
const [count, setCount] = useState(initialValue);
// 即使父组件传递的 prop 值 initialValue 发生改变
// count 状态仍然保持当前值,不会被重置
return (
<div>
<p>当前计数: {count}</p>
<button onClick={() => setCount(count + 1)}>增加</button>
</div>
);
}
根据 React 内部的协调(Reconciliation)后:
- 复用现有的组件实例
- 保持组件内部状态
- 仅使用新的 props 重新执行渲染逻辑