useState
const [state, setState] = useState(initialState);
返回一个 state,以及更新 state 的函数。在初始渲染期间,返回的状态 (state) 与传入的第一个参数 (initialState) 值相同。
setState 函数用于更新 state。它接收一个新的 state 值并将组件的一次重新渲染加入队列。
setState(newState);
在后续的重新渲染中,useState 返回的第一个值将始终是更新后最新的 state。
函数式更新
如果新的 state 需要通过使用先前的 state 计算得出,那么可以将函数传递给 setState。该函数将接收先前的 state,并返回一个更新后的值。
function Counter() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1)
}
function handleClickFn() {
setCount((prevCount) => {
return prevCount + 1
})
}
return (
<>
Count: {count}
<button onClick={handleClick}>+</button>
<button onClick={handleClickFn}>+</button>
</>
);
}
注意上面的代码,handleClick和handleClickFn一个是通过一个新的 state 值更新,一个是通过函数式更新返回新的 state。现在这两种写法没有任何区别,但是如果是异步更新的话,那你就要注意了,他们是有区别的,来看下面例子:
function Counter() {
const [count, setCount] = useState(0);
function handleClick() {
setTimeout(() => {
setCount(count + 1)
}, 3000);
}
function handleClickFn() {
setTimeout(() => {
setCount((prevCount) => {
return prevCount + 1
})
}, 3000);
}
return (
<>
Count: {count}
<button onClick={handleClick}>+</button>
<button onClick={handleClickFn}>+</button>
</>
);
}
当我设置为异步更新,点击按钮延迟到3s之后去调用setCount函数,当我快速点击按钮时,也就是说在3s多次去触发更新,但是只有一次生效,因为 count 的值是没有变化的。当使用函数式更新 state 的时候,这种问题就没有了,因为它可以获取之前的 state 值,也就是代码中的 prevCount 每次都是最新的值。
其实这个特点和类组件中 setState 类似,可以接收一个新的 state 值更新,也可以函数式更新。如果新的 state 需要通过使用先前的 state 计算得出,那么就要使用函数式更新。 因为setState更新可能是异步,当你在事件绑定中操作 state 的时候,setState更新就是异步的。当你在定时器中操作 state 的时候,而 setState 更新就是同步的。
注意这里的同步和异步指的是 setState 函数。因为涉及到 state 的状态合并,react 认为当你在事件绑定中操作 state 是非常频繁的,所以为了节约性能 react 会把多次 setState 进行合并为一次,最后在一次性的更新 state,而定时器里面操作 state 是不会把多次合并为一次更新的。
类组件
handleClick = () => {
this.setState({ count: this.state.count + 1 })
this.setState({ count: this.state.count + 1 })
// 这样写只会加1
}
handleClick = () => {
setTimeout(() => {
this.setState({ count: this.state.count + 1 })
this.setState({ count: this.state.count + 1 })
// 这样写是正常的,两次setState最后是加2
}, 3000);
}