React之修改state
React渲染
在进行首次渲染时 React 会调用根组件。调用目标 DOM 节点的 createRoot
,然后用你的组件调用 render
函数完成的:如下代码
const root = createRoot(document.getElementById('root'))
root.render(<Image />);
- 对于数据更新时React,会调用内部状态更新的触发渲染函数组件,在严格模式下,React 会执行每个更新函数两次(正常会丢弃第二个结果)以便帮助你发现错误。
UI更新
state改变后,会根据当前渲染时的 state计算出新的组件。
React 会等到事件处理函数中的 所有 代码都运行完毕再处理你的 state 更新。 这就是为什么重新渲染只会发生在所有这些 setNumber()
调用 之后 的原因。比如下面的代码中,第二次setNumber里的number还是初始的0,因为在本次渲染任务的事件处理函数里,number的值是0.
import { useState } from 'react';
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<button onClick={() => {
setNumber(number + 1);
setNumber(number + 1);
}}>+2</button>
</>
)
}
所以当想更新多个 state 变量——甚至来自多个组件的 state 变量——而不会触发太多的重新渲染时,我们可以传入更新函数,像setNumber(n => n + 1)
这样。只有在你的事件处理函数及其中任何代码执行完成 之后,UI 才会更新。这种特性也就是 批处理
更新函数执行原理
- React 会将此函数加入队列,以便在事件处理函数中的所有其他代码运行后进行处理。
- 在下一次渲染期间,React 会遍历队列并给你更新之后的最终 state。
模拟更新函数的批处理是如何执行的
export function getFinalState(baseState, queue) {
let finalState = baseState;
// baseState 初始值
// queue 要完成的队列
// getFinalState(baseState,[()=>{},()=>{},333])
queue.map(item=>{
if(typeof item !=='function'){
finalState=item
}else {
finalState=item(finalState)
}
})
return finalState;
}
更新state对象
- 如果state是多个属性的对象,更新时也是setState({x:xxx,y:xxx}),如果更新部分属性,则可以用扩展运算符来copy其他的属性。
const [user,setUser]=useState({
name:'a',
age:'b',
})
function handleNameChange(e) {
setPerson({
...user,
name:e.target.value
// [e.target.name]: e.target.value 可以用作表单绑定上的通用方法
});
}
-
层级深的对象:利用useImmer库,直接声明时使用
const [user,setUser]=useImmer({})
-
如果修改的state是数组
- 不直接修改数组,创建一个它的拷贝,然后使用新的数组来更新它的状态,
setState(newArray)
, - 用useImmer:这种方法并不是在直接修改原始的 state,而是在修改 Immer 提供的一个特殊的
draft
对象 - 用
[...arr, newItem]
数组展开符的方法来向数组中添加新元素 - 用
filter()
和map()
来创建一个新的经过过滤或者修改的数组。
- 不直接修改数组,创建一个它的拷贝,然后使用新的数组来更新它的状态,
转载自:https://juejin.cn/post/7244498421283831869