React函数组件-useState原理
问题引出:
一个函数组件首次渲染时,肯定会调用函数App()
,得到一个虚拟的DIV,进而创建真实的div到页面。
当用户点击button,更新一个数据时,会调用setN(n+1)
,肯定要重新渲染,所以肯定会再次调用App()
,得到新的虚拟DIV。
React对比两个DIV,局部更新。
在这个过程中,每次调用App函数,都会运行useState
。那么,App重新执行的时候,执行useState(0)
的结果,即n每次的值会不同吗?
functionApp() {const [n, setN] = React.useState(0);
console.log(`n:${n}`)
return (
<div className="App">
<p>{n}</p>
<p>
<button onClick={() => setN(n + 1)}>+1</button>
</p>
</div>
);
}
通过log,每次点击按钮,把n+1后,执行的App函数里,每次log出的n的值都是不同的。为什么同样的一句代码,在几次执行的过程中,会有不同的结果呢?
useState的原理和上图自己实现的代码类似。有一个state数组,存放一个函数组件里的所有数据。这些数据通过下标index区分。每个数据在调用useState()
的时候,都会按顺序有自己的index。调用这个函数返回的n就用来读取值。
点击按钮会调用setN(n+1)
,在这个函数里,把新的值赋给state数组里的对应项,然后再次渲染,只要渲染就肯定会调用App()函数,就又会执行一次useState
,再次执行的时候会做一次判断,如果state里已经有值了,就继续使用上次的值,也就是更新之后的值。这样就保证了,在多次渲染时,即使是执行了同一行代码useState(0)
,也会有最新的结果。
不同的组件都有一份属于自己的state和index,保存在自己的虚拟DOM上。如果点击了按钮,执行setState(),会修改state,并且触发更新,就会再次渲染,再次调用App(),就会再次执行useState,它会读取state[index],而且是最新值,然后生成新的虚拟DOM
setN(n+1)不会改变n
如果点击按钮,修改数据,setN是不会修改原来的n的,而是生成另一个新的n,这两个n是同时存在着的。也就是n是有分身的。
因为只要修改数据,就会触发UI更新,那就一定会再次调用App()
。比如第一次调用App,是第一次渲染,n是0.把n+1,就会第二次调用App,是第二次渲染,在这个新的App里,n是1.但是原来的App里的n还是0,并没有被改变。它只是生成了另一份新的数据。
useState的注意事项
1. 不能局部更新
functionApp() {const [user,setUser] = useState({name:'anqi', age: 18})
const onClick = ()=>{
setUser({
name: 'Jack'
})
}
return (
<div className="App">
<h1>{user.name}</h1>
<h2>{user.age}</h2>
<button onClick={onClick}>Click</button>
</div>
);
}
如果state是一个对象,里边有多个属性。在setUser的时候,如果只修改其中一个属性,会导致另一个变成undefined。因为setState不会自动合并属性。
应该用...
语法,把这个对象的所有属性先拷贝过来,再设你要改变的属性。
const onClick = ()=>{setUser({
...user,
age:20
})
}
2. 不能在原地址上修改
const onClick = ()=>{user.age=30
setUser(user)
}
比如说,我想修改user.age,先把user.age赋一个新值,在set这个user。会发现,点击按钮,UI并没有更新。
因为React发现user的地址没变,就认为数据没变,就不会去更新UI。
所以要给一个新的地址,给set里传一个对象就可以了,这样就不是同一个地址了。
3. useState也可以接受函数
const [state, setState] = useState(()=>{return initialState
})
返回一个初始值,效果和直接把初始值传进去一样。接受函数的好处是如果这个初始值的计算比较复杂,函数形式只会执行一次,也就只会在第一次计算。如果是直接传初始值,每次进来都要计算一次。(但是我们一般直接传初始值)
4. setState也可以接受函数
使用场景:我想先把n+1,再把n+2。如果像下边这样写,预期效果是点击按钮后,页面上的n 是3
functionApp() {const [n, setN] = useState(0)
const onClick = ()=>{
setN(n+1)
setN(n+2)
}
return (
<div className="App">
<h1>n: {n}</h1>
<button onClick={onClick}>+3</button>
</div>
);
}
可是点击按钮,发现n变成了2。
因为我们之前说过,setN(n+1)不会改变n,它会生成一个新的n。即,执行了第一行setN(n+1)
,此时n还是0,如果接下来又对这个n操作,setN(n+2)
,意思是把n加2,就是把0加2,那么结果就是2。不管setN有多少行,都只相当于执行最后一行。
如果想对一个state连续操作,就可以使用函数
const onClick = ()=>{setN(i=>i+1)
setN(i=>i+2)
}
setN里没有n这个变量。只有一个函数,这个函数代表了一个操作,+1操作,+2操作。并没说把n加1。只是用了占位符i
表示了一种操作。
React看到这两行,先把n+1,第二行再把新的值也就是n=1按照+2操作进行。所以页面上n变3
更推荐使用函数写法
以上是 React函数组件-useState原理 的全部内容, 来源链接: utcz.com/a/22835.html