在 react 开发中,对于某一个 state ,我们可能需要获取它的上一个状态的值,下面介绍两种方法来获取上一个 state 的值。
需求:假设页面上有一个计数器 count,点击按钮计数加1,需要在页面上展示当前 count 的值和上一个 count 的值。
我们知道 useState hook 可以让我们定义一个 state 和一个 state 更新函数 setState,setState 可以直接更新 state,也可以基于上一个 state 的值来更新 state,如 setCount(preValue => preValue+1),那么 preValue 就是我们想要的上一个 state 的值,示例代码如下:
function Counter(){
let [count, setCount] = useState(0)
let [preCount, setPreCount] = useState(0)
const _increaseCount = () => {
setCount(preValue => {
// 存储上一个状态的值
setPreCount(preValue)
return preValue + 1;
})
}
return <>
<div>Previous Count Value: {preCount}</div>
<div>Current Count Value: {count}</div>
<button onClick={increaseCount}> +1 </button>
</>
}
结果如下:点击按钮,count(当前 count) 和 preCount 同时加 1,且 count 始终比 preCount 大 1。
第一种方法虽然能满足要求,但是定义了两个 state (count 和 preCount),当点击按钮的时候,两个 state 的值都变了,导致页面更新了两次,页面多了一次不必要的更新。那么怎么才能在页面只更新一次的前提下实现需求呢?
我们分析一下,其实不把 preCount 作为 state 即可,不作为 state 后,preCount 的变化就不会引起页面的重新渲染。那在 react 中,什么样的数据变化之后,不会引起页面重新渲染呢?答案是:挂载在 useRef hook 上的数据。useRef 允许我们引用渲染时不需要的值。示例代码如下:
// usePrevious hook
function usePrevious(data) {
const ref = useRef(0);
useEffect(() => {
ref.current = data;
}, [data]);
return ref.current;
}
// Counter 组件
function Counter(){
let [count, setCount] = useState(0)
const _increaseCount = () => {
setCount(count+1)
}
// 从 usePrevious 取得 preCount
const preCount = usePrevious(count)
return <>
<div>Previous Count Value: {preCount}</div>
<div>Current Count Value: {count}</div>
<button onClick={increaseCount}> +1 </button>
</>
}
上述 usePrevious hook 中,data 作为入参,也就是我们的 state。使用了 useRef hook,把 data 挂载 ref 的 current 属性上,然后把 data 作为 useEffect 的依赖,当 data 发生改变时,把 data 重新赋值给 ref.current,这样一来,ref.current 就能随着 data 变化。最后 usePrevious hook 返回 ref.current。
最后在 Counter 组件中声明 const preCount = usePrevious(count),那么 preCount 就随着 count 的更新而更新,由于 preCount 不是组件 Counter的 state ,所以它的更新不会触发组件重新渲染,只有身为组件 state 的 count 的变化才会触发组件重新渲染,这样一来就避免了不必要的更新。