React Hooks useState返回的setState方法不生效怎么办?
如果你在使用useState hook返回的set方法时,发现set之后,读取对应状态并没有改变,本文是可能的原因。
上个例子:
import React, {useState} from 'react';
import {render} from 'react-dom';
function Test() {
const [foo, setFoo] = useState(0);
return (
onClick={() => {
setFoo(1);
console.log('foo', foo);
render(
(<Test/>),
document.body
当点击链接时,我们设置foo为1,期望console输出foo为1,但实际上第一次点击时,输出foo还是初始值0,仿佛setFoo(1)失效了一样。
我们分析原因,直觉上会认为useState返回的set方法是异步的,所以这种set之后同步读取state的代码会读到旧的值。
我们给set之后读取state的操作加个timeout试试:
import React, {useState} from 'react';
import {render} from 'react-dom';
function Test() {
const [foo, setFoo] = useState(0);
return (
onClick={() => {
setFoo(1);
setTimeout(() => {
console.log('foo after 1s', foo);
}, 1000);
render(
(<Test/>),
document.body
我们第一次点击完等了一秒之后输出还是0,这又是为什么呢?
其实我们第一步分析的原因“useState返回的set方法是异步的”是对的,只不过set方法不生效还有更深层次的原因。
我们知道React Hooks本质上就是函数,我们在这里读取的foo变量是本轮渲染时useState的返回值,foo变量的值在 const[foo, setFoo]=useState(0); 这句代码执行的时候就确定了。
我们使用setFoo方法改变的是组件的名字为foo的state,但是并没有改变本轮渲染中foo变量的值,所以即使是把输出foo的语句放到了setTimeout里面,它依然指向不变的foo变量,而非组件的foo state。
那有什么办法让我们在函数组件中修改了state后能读取到最新的state吗?如果是Class形式定义的组件的话,我们还可以在 setState(updater, [callback]) 的可选参数callback中使用 http:// this.state.xxx 的方式获取到最新的state,但是useState hook返回的set方法去掉了第二个回调参数。
网上有一种 比较流行的方法 是把修改state之后读取state的操作放到useEffect中去做,我认为这种方法很不直观。
这里我提供另一种方法:
import React, {useState} from 'react';
import {render} from 'react-dom';
function Test() {
const [foo, setFoo] = useState(0);
return (
onClick={() => {
setFoo(1);
setFoo(prevFoo => {
console.log('read foo state in setFoo function', prevFoo);
return prevFoo;
render(
(<Test/>),
document.body
useState hook返回的set方法除了 setState(newState); 这种调用方法,还可以接受一个函数参数,用旧的state值来计算出新的state返回:
setState(prevState => {
const newState = computeUpdatedState(prevState);
return newState;