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

回到顶部