回调函数中useState值有误

回调函数中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

二、解决思路

  1. 判断setId方法是否生效,id值是否更改;
  2. 判断打印时获取的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>)
}

猜你喜欢

转载自blog.csdn.net/IO14122/article/details/126536410