useGetState自定义hooks解决useState 异步回调获取不到最新值

setState 的两种传参方式

1、直接传入新值 setState(options);

const [state, setState] = useState(0);
setState(state + 1);

2、传入回调函数 setState(callBack);

const [state, setState] = useState(0);
setState((prevState) => prevState + 1); // prevState 是改变之前的 state 值,return 返回的值会作为新状态覆盖 state 值

useState 异步回调获取不到最新值及解决方案

通常情况下 setState 直接使用上述第一种方式传参即可,但在一些特殊情况下第一种方式会出现异常; 例如希望在异步回调或闭包中获取最新状态并设置状态,此时第一种方式获取的状态不是实时的,React 官方文档提到:组件内部的任何函数,包括事件处理函数和 Effect,都是从它被创建的那次渲染中被「看到」的,所以引用的值任然是旧的,最后导致 setState 出现异常:

import React, { useState, useEffect } from 'react';const App = () => {const [arr, setArr] = useState([0]);useEffect(() => {console.log(arr);}, [arr]);const handleClick = () => {Promise.resolve().then(() => {setArr([...arr, 1]); // 此时赋值前 arr 为:[0]}).then(() => {setArr([...arr, 2]); // 此时赋值前 arr 为旧状态仍然为:[0]});}return (<><button onClick={handleClick}>change</button></>);
}export default App;// 输出结果
[0]
[0,1]
[0,2]

上面代码,App 组件实际也是个闭包函数,handleClick 里面引用着 arr,第一次 setArr 后 arr 的值确实更新了,我们也可以在上面输出结果中看到,但此次执行的 handleClick 事件处理函数作用域还是旧的,里面引用的 arr 仍然为旧的,导致第二次 setArr 后结果为 [0, 2]:

在 class 组件中我们可以使用 setState(options, callBack); 在 setState 的第二个参数回调函数中再次进行 setState,也不存在闭包作用域问题,但是 React Hook 中 useState 移除了 setState 的第二个参数,而且若嵌套太多也不佳;

解决方案1

const handleClick = () => {Promise.resolve().then(() => {setArr(prevState => [...prevState, 1]); // 这里也可以不改,使用第一中传参方式 setArr([...arr, 1]); 因为这里不需要获取最新状态}).then(() => {setArr(prevState => [...prevState, 2]); // 这里必须改成回调函数传参方式,否则会读取旧状态,导致异常});}// 输出结果
[0]
[0,1]
[0,1,2]

我们发现用回调方式传参,输出结果是正确的。

解决方案2:

使用 useReducer 仿造类组件中的 forceUpdate 实现组件强制渲染; 注意: 此方案仅限于只有页面依赖该数据时适用,如果有类似 useEffect 等 hook 在监听该数据(示例中的 arr )时无法实时捕捉到变化

import React, { useState, useReducer } from 'react';const App = () => {const [arr, setArr] = useState([0]);const [, forceUpdate] = useReducer(x => x + 1, 0);const handleClick = () => {Promise.resolve().then(() => {arr.push(1); // 如果这里也需要做一次渲染在改变状态后调用 forceUpdate() 即可}).then(() => {arr.push(2);forceUpdate();});}return (<><h1>{arr.toString()}</h1><button onClick={handleClick}>change</button></>);
}export default App;

解决方案3:

利用 ref ,state 发生改变同时将值映射到 ref ref 的改变不会触发页面更新,但在异步中一定能拿到最新值,所以需要在页面上用就使用 state,在异步逻辑中用就使用 ref

import React, { useState, useRef, useEffect } from 'react';const App = () => {const [arr, setArr] = useState([0]);let ref = useRef();useEffect(() => {ref.current = arr;console.log(arr);}, [arr]);const handleClick = () => {Promise.resolve().then(() => {const now = [...ref.current, 1];ref.current = now;setArr(now);}).then(() => {setArr([...ref.current, 2]);});}return (<><h1>{arr.toString()}</h1><button onClick={handleClick}>change</button></>);
}export default App;

方案四,推荐方案:

上面例3这类方式可以自己封装一个 hooks 将 state 和 ref 进行关联,同时再提供一个方法供异步中获取最新值使用,例如:

const useGetState = (initVal) => {const [state, setState] = useState(initVal);const ref = useRef(initVal);const setStateCopy = (newVal) => {ref.current = newVal;setState(newVal);}const getState = () => ref.current;return [state, setStateCopy, getState];
}const App = () => {const [arr, setArr, getArr] = useGetState([0]);useEffect(() => {console.log(arr);}, [arr]);const handleClick = () => {Promise.resolve().then(() => {setArr([...getArr(), 1]);}).then(() => {setArr([...getArr(), 2]);});}return (<><h1>{arr.toString()}</h1><button onClick={handleClick}>change</button></>);
}

遇到useState 异步回调获取不到最新值的时候,推荐方案1和方案4,方案1解决更加简单,方案4解决更加完美,都推荐,看你的使用场景了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/78275.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【网络教程】超越平凡:一文揭示SSH-keygen的神秘世界

SSH(Secure Shell)是一种网络协议,用于安全地连接到远程计算机。SSH-keygen 是 SSH 协议的一部分,用于生成、管理和转换身份验证密钥对。 SSH-keygen 命令的基本语法如下: ssh-keygen [选项]以下是 ssh-keygen 命令的一些常用选项和参数: -t:指定要生成的密钥类型。例如…

Python实现猎人猎物优化算法(HPO)优化Catboost分类模型(CatBoostClassifier算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 猎人猎物优化搜索算法(Hunter–prey optimizer, HPO)是由Naruei& Keynia于2022年提出的一种最新的…

基于BLIP-2的看图问答原理及实现

大型语言模型 (LLM) 最近获得了很大的关注&#xff0c;出现了许多流行的模型&#xff0c;如 GPT、OPT、BLOOM 等。 这些模型擅长学习自然语言&#xff0c;非常适合构建聊天机器人、编码助手、决策助手或翻译系统。 然而&#xff0c;他们缺乏其他模式的知识—例如&#xff0c;他…

GIS地图服务数据可视化

GIS地图服务数据可视化 OSM&#xff08;Open Street Map&#xff0c;开放街道地图&#xff09;Bing地图&#xff08;必应地图&#xff09;Google地图&#xff08;谷歌地图&#xff09; 地图服务数据可视化是根据调用的地图服务请求Web服务器端的地图数据&#xff0c;实现地图数…

python自学

自学第一步 第一个简单的基础&#xff0c;向世界说你好 启动python 开始 print是打印输出的意思&#xff0c;就是输出引号内的内容。 标点符号必须要是英文的&#xff0c;因为他只认识英文的标点符号。 exit&#xff08;&#xff09;推出python。 我们创建一个文本文档&…

SpringBoot+MySQL+Vue前后端分离的宠物领养救助管理系统(附论文)

文章目录 项目介绍主要功能截图:后台:登录个人中心宠物用品管理宠物领养管理用户管理用户领养管理宠物挂失管理论坛管理系统管理订单管理前台首页宠物挂失论坛信息宠物资讯部分代码展示设计总结项目获取方式🍅 作者主页:超级无敌暴龙战士塔塔开 🍅 简介:Java领域优质创

前端面试的话术集锦第 6 篇:高频考点(事件机制 跨域 存储机制 浏览器缓存等)

这是记录前端面试的话术集锦第六篇博文——高频考点(事件机制 & 跨域 & 存储机制 & 浏览器缓存等),我会不断更新该博文。❗❗❗ 1. ⼿写 call、apply 及 bind 函数 ⾸先从以下⼏点来考虑如何实现这⼏个函数: 不传⼊第⼀个参数,那么上下⽂默认为window 改变了…

Java 复习笔记 - 常用API 下

文章目录 一&#xff0c;JDK7以前时间相关类&#xff08;一&#xff09;Date 时间&#xff08;二&#xff09;SimpleDateFormat 格式化时间&#xff08;三&#xff09;Calendar 日历 二&#xff0c;JDK8新增时间相关类&#xff08;一&#xff09;时区、时间和格式化&#xff08…

Layui + Flask 使用(01)

Layui 是一套开源免费的 Web UI 组件库,采用自身轻量级模块化规范,遵循原生态的 HTML/CSS/JavaScript 开发模式,极易上手,拿来即用,非常适合网页界面的快速构建。在使用了很久之后,也发现了一些问题。 先说优点: layui 采用的是原生的 HTML/CSS/JavaScript 技术开发,上…

R-YOLOv7-tiny检测浸水玉米胚乳裂纹

Detecting endosperm cracks in soaked maize using μCT technology and R-YOLOv7-tiny 1、模型1.1 C3_TR module(自己提出修改)1.2 CoT block注意力1.3 GhostConv模块2、模型整体流程图3、实验采用r - yolov7微模型和μCT技术对浸水玉米胚乳裂纹进行了检测。提出的ryolov7微…

Nginx参数配置详细说明【全局、http块、server块、events块】【已亲测】

Nginx重点参数配置说明 本文包含Nginx参数配置说明全局块、http块、server块、events块共计30多个参数配置与解释&#xff0c;其中常见参数包含配置错误出现的错误日志&#xff0c;能让你更快的解决问题。 该文的所有参数大部分经过单独测试&#xff0c;错误都是自己收集出来的…

iOS开发 蓝牙功能

iOS开发蓝牙功能主要分扫描中心和外设设备 Central: 中心设备,发起蓝牙连接的设备(一般指手机)Peripheral: 外设,被蓝牙连接的设备(一般是运动手环/蓝牙模块)Service:服务,每个设备会提供服务,一个设备有很多服务Characteristic:特征,每个服务中包含很多个特征,这些特征的权限…

付费进群搭建二维码

如今&#xff0c;在互联网时代&#xff0c;群组聊天已经成为人们高效沟通和合作的重要方式。然而&#xff0c;对于一些专业团队或机构来说&#xff0c;免费的系统可能无法满足他们对安全性、稳定性和个性化功能的要求。因此&#xff0c;他们需要寻找付费的群组系统&#xff0c;…

如何将安防视频监控系统/视频云存储EasyCVR平台推流到公网直播间?

视频云存储/安防监控EasyCVR视频汇聚平台基于云边端智能协同&#xff0c;支持海量视频的轻量化接入与汇聚、转码与处理、全网智能分发、视频集中存储等。音视频流媒体视频平台EasyCVR拓展性强&#xff0c;视频能力丰富&#xff0c;具体可实现视频监控直播、视频轮播、视频录像、…

基于PyTorch使用LSTM实现新闻文本分类任务

本文参考 PyTorch深度学习项目实战100例 https://weibaohang.blog.csdn.net/article/details/127154284?spm1001.2014.3001.5501 文章目录 本文参考任务介绍做数据的导入 环境介绍导入必要的包介绍torchnet和keras做数据的导入给必要的参数命名加载文本数据数据前处理模型训…

防火墙概述及实战

目录 前言 一、概述 &#xff08;一&#xff09;、防火墙分类 &#xff08;二&#xff09;、防火墙性能 &#xff08;三&#xff09;、iptables &#xff08;四&#xff09;、iptables中表的概念 二、iptables规则匹配条件分类 &#xff08;一&#xff09;、基本匹配条…

echarts常用参数详解汇总(饼图,柱形图,折线图)持续更新中

常用配置&#xff1a; X/Y轴线的基础设置《通用》 细微的差距只能去官网查看了&#xff0c;基本一致 这里只是做了个汇总方便查看 xAxis/yAxis: {show:false, // 不显示坐标轴线、坐标轴刻度线和坐标轴上的文字axisTick:{// 不显示坐标轴刻度线show:false, alignWithLabel: tru…

sklearn中的数据集使用

导库 from sklearn.datasets import load_iris 实现 # 加载数据集 iris load_iris() print(f查看数据集&#xff1a;{iris}) print(f查看数据集的特征&#xff1a;{iris.feature_names}) print(f查看数据集的标签&#xff1a;{iris.target_names}) print(f查看数据集的描述…

看板管理:以可视化方式确定任务优先级

确定工作的优先级是我们今天都要面对的挑战。若处理不当&#xff0c;我们就可能试图一心多用&#xff0c;从而严重损害工作效率。 使用看板方法来设定工作优先级是一种非常直观、快速的方法。 确定工作优先级的看板方法 看板工作流程管理方法的核心在于工作可视化。工作被划…

linux学习总结

shell 1.在文本环境下&#xff0c;shell作为命令解释器&#xff0c;建立了用户和操作系统之间的接口。当用户键入一个命令时&#xff0c;shell将对该命令进行解释&#xff0c;并调用相应的程序。2.Linux下有多个shell&#xff0c;最常用的3个shell: bash tcsh zsh3.shell …