1、概念
const [state, setState] = useState(initialState)
返回一个 state,以及更新 state 的函数。在初始渲染期间,返回的状态 (state
) 与传入的第一个参数 (initialState
) 值相同。setState
函数用于更新 state。它接收一个新的 state 值并将组件的一次重新渲染加入队列。
setState(newState);
在后续的重新渲染中,useState 返回的第一个值将始终是更新后最新的 state。
2、默认值
useState
支持我们在调用的时候直接传入一个值,来指定 state
的默认值,比如这样 useState(0)
, useState({ a: 1 })
, useState([ 1, 2 ])
,还支持我们传入一个函数,来通过逻辑计算得出默认值,useState
中的函数只会执行一次。
function App (props) {
const [ count, setCount ] = useState(() => {
return props.count || 0
})
return (
<div>
点击次数: { count }
<button onClick={() => { setCount(count + 1)}}>点我</button>
</div>
)
}
3、函数式更新
如果新的 state 需要通过使用先前的 state 计算得出,那么可以传递一个函数给 setState
。该函数将接收先前的 state,并返回一个更新后的值。下面的计数器组件示例展示了 setState
的两种用法:
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 每次都是最新的值。
4、多个 useState 的情况
useState 我们不可能只使用一个,当我们使用多个 useState 的时候,那 react 是如何识别哪个是哪个呢,其实很简单,它是靠第一次执行的顺序来记录的,就相当于每个组件存放 useState 的地方是一个数组,每使用一个新的 useState,就向数组中 push 一个 useState。所以,不要在循环,条件或嵌套函数中调用 useState
, 确保总是在你的 React
函数的最顶层调用他们。
let index = 0
const memoizedStates = []
function useState (initialState) {
memoizedStates[index] = memoizedStates[index] || initialState
let currentIndex = index
function setState (newState) {
memoizedStates[currentIndex] = newState
render()
}
return [memoizedStates[index++], setState]
}