回调函数中useState值有误
一、问题产生
有这样一个场景:一个后台的接口查询时间过长,当返回成功时调用其他函数同时将useState值id当做当做参数传递,但在等待接口返回时这个id已经被更改过了;这时候bug就出现了,当把id当做参数传递时,此时的id还是接口调用时的值。
伪代码:
import React, { useState} from 'react';
import ReactDOM from 'react-dom';
const DomeApp = (props) => {
const [id, setId] = useState(1);
// 接口调用
const requestApi = () => {
// 利用setTimeout模拟接口请求
setTimeout(() => {
console.log(id, "setTimeout======");
}, 5000);
};
return
(<div>
<button onClick={requestApi}>发起请求</button>
<button onClick={() => setId((cur) => cur + 1)}>id更改</button>
</div>)
}
上方代码当点击发起请求
后,又迅速点击两次id更改
;5秒过后打印id为1
二、解决思路
- 判断setId方法是否生效,id值是否更改;
- 判断打印时获取的id值是从那个作用域拿到的;
三、实际操作
我们需要改造一下上面的代码:
import React, { useState} from 'react';
import ReactDOM from 'react-dom';
const DomeApp = (props) => {
const [id, setId] = useState(1);
// 接口调用
const requestApi = () => {
console.log(id, "requestApi======");
// 利用setTimeout模拟接口请求
setTimeout(() => {
console.log(id, "setTimeout======");
}, 5000);
};
return (<div>
<h2>{id}</h2>
<button onClick={requestApi}>发起请求</button>
<button onClick={() => setId(id+1)}>id更改</button>
</div>)
}
操作流程: 点击发送请求
后,迅速点击两次id更改
;此时页面上id随之增加,排除setId未生效
;而在接口函数中第一次打印id为1,5秒后依然是1
;
结论: 5秒后获取的id值是调用requestApi函数作用域里面的id值,并未获取DomeApp内定义的id值。
四、问题原因
在组件内部的任何函数,包括事件处理函数和 effect,都是从它被创建的那次渲染中被「看到」的。通俗的讲,每次setState后都产生一个新的状态,在requestApi函数中setTimeout里访问的就是旧状态id值。
具体请看官方文档:为什么我会在我的函数中看到陈旧的 props 和 state ?
五、解决方案
使用ref
import React, { useState, useEffect, useRef} from 'react';
import ReactDOM from 'react-dom';
const DomeApp = (props) => {
const [id, setId] = useState(1);
const ref = useRef();
ref.current = id;
// useEffect(() => {
// ref.current = id;
// });
// 接口调用
const requestApi = () => {
console.log(id,ref.current, "requestApi======");
// 利用setTimeout模拟接口请求
setTimeout(() => {
console.log(ref.current, "setTimeout======");
}, 5000);
};
return (<div>
<h2>{id}</h2>
<button onClick={requestApi}>发起请求</button>
<button onClick={() => setId(id+1)}>id更改</button>
</div>)
}