回调函数中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>)
}
// 文件上传组件plannedWork // 当前最新的单个文件 const [plannedFile, setPlannedFile] = useState<UploadFile | null>(null); const [currentPlannedFile, setCurrentPlannedFile] = useState<File | null>(null); const [plannedMessage, setPlannedMessage] = useState<{ type: ‘success’ | ‘error’; content: string } | null>( null ); const [plannedError, setPlannedError] = useState(false); const propsPlanned: UploadProps = { // 确保最多只能选择一个文件 maxCount: 1, beforeUpload: (file) => { // 保存当前选择的文件对象 setCurrentPlannedFile(file); // 阻止默认的自动上传行为 return false; }, onChange: ({ file }) => { if (file.status === 'done') { // 更新到最新文件 setPlannedFile(file as unknown as UploadFile); } }, }; useEffect(() => { if (plannedMessage) { // 消息提示持续时间 const timer = setTimeout(() => { setPlannedMessage(null); }, 5000); return () => clearTimeout(timer); } }, [plannedMessage]); const handlePlannedUpload = async () => { if (!inputnum) { setPlannedMessage({ type: ‘error’, content: “请先输入月份” }); return; } if (!currentPlannedFile) { setPlannedMessage({ type: 'error', content: "请先选择文件" }); return; } const formData = new FormData(); formData.append('file', currentPlannedFile!); try { const response = await axiosPost( `/user/plannedWork/read?date=${inputnum}-01`, formData, t, "multipart/form-data" ); if (response.data.code !== 200) { setPlannedMessage({ type: 'error', content: "文件内容有误" }); setPlannedError(true) } else { setPlannedMessage({ type: 'success', content: "上传成功" }); setUp1(true); setPlannedError(false) // 清空已选文件 setPlannedFile(null); setCurrentPlannedFile(null); } } catch (error: any) { setPlannedMessage({ type: 'error', content: error.response?.data?.message || "上传失败", }); setPlannedError(true) } };帮我实现在执行handlePlannedUpload后判断plannedError,如果plannedError是true,file.status === ‘error’
最新发布
04-04
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

懒羊羊我小弟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值