函数组件useState用法 以及 useState异步回调获取不到最新值

目录

 1、useState用法: 三个参数用法

 2、如何监听state的变化

 3、dispatch更新特点

 4、解决上述demo中使用useState 异步回调获取不到最新值

4.1   下面这种情况的写法,仍获取不到最新值:

4.2   解决方案1:dispatch参数,作为函数。

4.3  解决方案2

4.4  解决方案3


类组件中定义state,通过this.setState更新state里的值。而函数组件中的useState可以使函数组件像类组件一样拥有state。

类组件中通过this.setState更新state,进而改变UI视图。函数组件中通过useState来改变UI视图。

 1、useState用法: 三个参数用法

import React, { useState } from 'react';const  A  =  props => {const [state, dispatch] = useState(initData)}
export default A
  • state:目的提供给 UI ,作为渲染视图的数据源。
  • dispatch:改变 state 的函数,推动函数组件渲染的渲染函数

                      第一种:作为非函数,作为新的值,赋予给 state,作为下一次渲染使用

const [ count, setCount ] = React.useState(0)// 点击事件
const handleClick=()=>{setNumber(1)
}
console.log(count, 'count') // 1

                      第二种:作为函数,这里可以称它为reducer,reducer 参数,是上一次返回最新的 state,返回值作为新的 state。

const [ number , setNumber ] = React.useState(0)
const handleClick=()=>{setNumber((state)=> state + 1)  // state - > 0 + 1 = 1setNumber(8)  // state - > 8setNumber((state)=> state + 1)  // state - > 8 + 1 = 9
}
  • initData:有两种情况。

                     第一种:作为非函数,作为 state 初始化的值。

const [count, setCount] = useState(0)

                     第二种:作为函数,函数的返回值作为 useState 初始化的值

// props中的参数c小于数字5, 返回0,即count=0。 
// c小于数字10,大于5, 返回1,即count=1。
// c大于10,返回2,即count=2const [count, setCount] = React.useState(() => {if(props.c < 5) return 0if(props.c < 10) return 1return 2
})

 2、如何监听state的变化

类组件 setState 中,有第二个参数 callback 或者是生命周期componentDidUpdate 可以检测监听到 state 改变或是组件更新。

在函数组件中,通过useEffect监听。把 state 作为依赖项传入 useEffect 第二个参数 deps ,但是注意 useEffect 初始化会默认执行一次。

import React, { useState } from 'react';export default function A (props){const [ number , setNumber ] = useState(0)/* 监听 number 变化 */React.useEffect(()=>{console.log('监听number变化,此时的number是:  ' + number )},[ number ])const handerClick = ()=>{/** 高优先级更新 **/ReactDOM.flushSync(()=>{setNumber(2) })/* 批量更新 */setNumber(1) /* 滞后更新 ,批量更新规则被打破 */setTimeout(()=>{setNumber(3) })} console.log(number)return <div><span> { number }</span><button onClick={ handerClick }>点击</button> </div>
}//  2
//  监听number变化,此时的number是: 2
//  1
//  监听number变化,此时的number是: 1
//  3
//  监听number变化,此时的number是: 3

 3、dispatch更新特点

在使用useState 有一点值得注意,当调用改变 state 的函数dispatch,函数组件中dispatch 更新效果和类组件是一样的,是获取不到最新的 state 值的

const [ number , setNumber ] = React.useState(0)
const handleClick = ()=>{ReactDOM.flushSync(()=>{setNumber(2) console.log(number, '2') })setNumber(1) console.log(number, '1')setTimeout(()=>{setNumber(3) console.log(number, '3')})   
}// 0  '2'
// 0  '1'
// 0  '3'

 4、解决上述demo中使用useState 异步回调获取不到最新值

useState中的dispatch参数,既可以作为非函数,也可以作为函数。

4.1   下面这种情况的写法,仍获取不到最新值:

        本应输出 [0, 1, 2],打印结果却不是

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]   useEffect 初始化会默认执行一次
[0,1]
[0,2]// 例2:const [number, setNumber] = useState([0]);const handleClick= () => {setTimeout(()=>{setNumber([...number, 1])},100)setTimeout(()=>{setNumber([...number, 2])},500)}useEffect(() => {console.log(number, 'number' )}, [number]);return (<><button onClick={handleClick}>change</button></>);// [0]  useEffect 初始化会默认执行一次
// [0,1]
// [0,2]

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

4.2   解决方案1:dispatch参数,作为函数。
import React, { useState, useEffect } from 'react';const App = () => {const [arr, setArr] = useState([0]); // 声明一个数组 const arr = [0]useEffect(() => {console.log(arr);}, [arr]);const handleClick = () => {Promise.resolve().then(() => {setArr(prevState => [...prevState, 1]); // 或者这样写:setArr([...arr, 1]); }).then(() => {setArr(prevState => [...prevState, 2]); // 这里必须改成回调函数传参方式,否则会读取旧状态,导致异常});}return (<><button onClick={handleClick}>change</button></>);
}export default App;// 输出结果
[0]   useEffect 初始化会默认执行一次
[0,1]
[0,1,2]// 例2:const [fields, setfields] = React.useState([0]);
const intervalRef = React.useRef();
function change(){setTimeout(()=>{setfields([...intervalRef.current, 1])},100)setTimeout(()=>{setfields([...intervalRef.current, 2])},500)
}React.useEffect(() => {intervalRef.current = fields;console.log(fields, '123' )
}, [fields]);// [0]  '123'    useEffect 初始化会默认执行一次
// [0,1]  '123'
// [0,1,2]  '123'
4.3  解决方案2

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

import React, { useState, useRef, useEffect } from 'react';const App = () => {const [arr, setArr] = useState([0]);let refArr = useRef(); // 1. 声明一个refArruseEffect(() => {refArr.current = arr; // 2. 把最新的arr的值赋值给refArr.current。第一次时refArr.current = [0]console.log(arr);}, [arr]);const handleClick = () => {Promise.resolve().then(() => {const now = [...refArr.current, 1]; // 3. ...拓展运算符展开refArr.current,然后再与1进行合并,声明一个变量now接受新合并的值,得到 const now = [0,1],setArr(now);  // 4. 通过setArr更新arr数组refArr.current = now;  // 5. 这一步代码必须有,也是最关键的一步,这一步代码中把拿到最新now的值 再 重新赋值给refArr.current,保证了第6步中2能添加进去。如果不写这一步,里面引用的 arr 仍然为旧的,导致第二次 setArr 后结果为 [0, 2]}).then(() => {setArr([...ref.current, 2]);  // 6. ...拓展运算符展开refArr.current,然后再与2进行合并});}return (<><h1>{arr.toString()}</h1><button onClick={handleClick}>change</button></>);
}export default App;// [0]   useEffect 初始化会默认执行一次
// [0,1]
// [0,1,2]// 例2 
const [fields, setfields] = React.useState([0]);
const intervalRef = React.useRef();React.useEffect(() => {intervalRef.current = fields;console.log(fields, '123' )
}, [fields]);function change(){setTimeout(()=>{setfields([...intervalRef.current, 1])},100)setTimeout(()=>{setfields([...intervalRef.current, 2])},500)
}// [0]  '123'
// [0,1]  '123'
// [0,1,2]  '123'
4.4  解决方案3

        优化方案2:

封装一个 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></>);
}

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

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

相关文章

超级菜鸟怎么学习数据分析?

如果你有python入门基础&#xff0c;在考虑数据分析岗&#xff0c;这篇文章将带你了解&#xff1a;数据分析人才的薪资水平&#xff0c;数据人应该掌握的技术栈。 首先来看看&#xff0c;我在搜索数据分析招聘时&#xff0c;各大厂开出的薪资&#xff1a; 那各大厂在数据领域…

DC电源模块的特点及应用案例分享

BOSHIDA DC电源模块的特点及应用案例分享 DC电源模块是一种可以将交流电转换为直流电的设备&#xff0c;具有以下特点&#xff1a; 1.高效稳定&#xff1a;DC电源模块采用高效稳定的电源转换技术&#xff0c;可以将输入的交流电转换为输出的稳定直流电&#xff0c;并且具有高…

什么是游戏盾?哪家效果好。

游戏盾是什么呢&#xff0c;很多做游戏开发的客户估计都是听说过的&#xff0c;但是也不是所有的游戏开发者会运用到。因为&#xff0c;游戏盾是针对游戏行业APP业务所推出的高度可定制的网络安全管理解决方案&#xff0c;除了能针对大型DDoS攻击(T级别)进行有效防御外&#xf…

动态规划学习——机器人运动

//一共有N个位置&#xff0c;机器人从start开始&#xff0c;走K步到end //机器人到1后只能向2运动&#xff0c;到N后只能向N-1运动&#xff0c;即不能越界&#xff0c;只能在1-N的位置运动 //求总的路线的个数 //例&#xff1a; //N4,startp1,endp3,K4 //则路线如下&#xff1a…

Leetcode的AC指南 —— 栈与队列:232.用栈实现队列

摘要&#xff1a; **Leetcode的AC指南 —— 栈与队列&#xff1a;232.用栈实现队列 **。题目介绍&#xff1a;请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作&#xff08;push、pop、peek、empty&#xff09;&#xff1a; 实现 MyQueue 类&#xff1a;…

列表列添加千分位保留两位小数

// 千分位无小数部分 function setThousandsMarkNoDecimal(num) {// console.log(num????, num, typeof num)if (!num) return num;let fu false;if (num.toString().includes(-)) {fu true;num Number(num.toString().substr(1));}// num Math.trunc(num); // 保留整数…

【Spring 篇】MyBatis注解开发:编写你的数据乐章

欢迎来到MyBatis的音乐殿堂&#xff01;在这个充满节奏和韵律的舞台上&#xff0c;注解是我们编写数据乐章的得力助手。无需繁琐的XML配置&#xff0c;通过简单而强大的注解&#xff0c;你将能够轻松地与数据库交互。在这篇博客中&#xff0c;我们将深入探讨MyBatis注解开发的精…

华为OD机试 - 智能驾驶(Java JS Python C)

题目描述 有一辆汽车需要从 m * n 的地图左上角(起点)开往地图的右下角(终点),去往每一个地区都需要消耗一定的油量,加油站可进行加油。 请你计算汽车确保从从起点到达终点时所需的最少初始油量。 说明: 智能汽车可以上下左右四个方向移动地图上的数字取值是 0 或 -1 …

NLP自然语言处理实战

一、自然语言处理的概念 1.1 自然语言处理的起源 语言是人类社会发展过程的产物&#xff0c;是最能体现人类智慧和文明的证明&#xff0c;也是人类与动物最大的区别。它是一种人与人交流的载体&#xff0c;像计算机网络一样&#xff0c;我们使用语言相互传递知识。在人类历史…

潜水泵如何实现远程状态监测与预测性维护?

在各行各业&#xff0c;潜水泵的健康数据采集一直是一项具有挑战性的任务。然而&#xff0c;一项被称为电气特征分析&#xff08;ESA&#xff09;的技术通过在电机控制柜而非泵本身上安装传感器&#xff0c;成功解决了这一问题。 图.泵&#xff08;iStock&#xff09; 一、电气…

Docker部署Golang服务

不管是开发还是生产环境&#xff0c;通过 docker 方式部署服务都是一种不错的选择&#xff0c;能够解决不同开发环境一致性的问题。 本文以项目&#xff1a;https://github.com/johncxf/go_practice 为例。 Dockerfile 构建 Go 运用环境 在项目根目录下添加 Dockerfile 文件…

Ubuntu重设root的密码

重设root的密码 未重设密码之前&#xff0c;Ubuntu 中默认的 root 密码是随机的&#xff0c;即每次开机都会有一个新的root 密码&#xff0c;所以此时的 root 用户密码并不确定&#xff1b; 重设root 密码&#xff0c;使用安装时创建的用户登录后sudo su切换至root用户&#…

云服务器搭建coturn出现Not reachable?

文章目录 问题复现解决方案1. 云服务器端口开放问题2. 检查配置文件3. 浏览器 问题解决 问题复现 使用云服务器搭建coturn服务时&#xff0c;出现not reachable报错 ICE Server配置是正确的 但测试relay时却报错&#xff1a;not reachable? 并且服务器也没输出相应日志。 …

(2021|ICLR,扩散先验,VE-SDE,逼真和忠实的权衡)SDEdit:使用随机微分方程引导图像合成和编辑

SDEdit: Guided Image Synthesis and Editing with Stochastic Differential Equations 公和众和号&#xff1a;EDPJ&#xff08;进 Q 交流群&#xff1a;922230617 或加 VX&#xff1a;CV_EDPJ 进 V 交流群&#xff09; 目录 0. 摘要 2. 背景&#xff1a;使用随机微分方程…

vue3-生命周期

生命周期 生命周期 vue 组件实例都有自己的一个生命周期 从创建->初始化数据->编译模版->挂载实例到 DOM->数据变更后更新 DOM ->卸载组件 生命周期简单说就是 vue 实例从创建到销毁的过程 生命周期钩子 在各个周期运行时&#xff0c;会执行钩子函数&…

vue 函数化组件

Vue.js提供了一个functional的布尔值选项&#xff0c;设置为true可以使组件无状态和无实例&#xff0c;也就是没有data和this上下文。这样用render函数返回虚拟节点可以更容易渲染&#xff0c;因为函数化组件只是一个函数&#xff0c;渲染开销要小很多。 使用函数化组件时&…

文件改名大师,随机大小写字母混合,让你的文件名独一无二!

文件重命名&#xff0c;单调又乏味&#xff1f;现在&#xff0c;有了我们的文件改名大师&#xff0c;让你的文件名充满惊喜与创意&#xff01;厌倦了那些毫无新意的文件名&#xff1f;我们为你提供了一种全新的方式&#xff0c;将文件名随机改名为大小写字母混合&#xff0c;让…

C语言实战系列一:经典贪食蛇

C语言学习必须实战&#xff0c;并且学完语法后就必须立即用实战来巩固。一般需要10来个比较复杂的程序才能掌握C语言。今天就教大家第一个小程序&#xff0c;贪食蛇。 首先上代码 一、代码 #include <stdio.h> #include <stdlib.h> #include <curses.h> #…

Springboot自动装配:三个注解、Selector、spring.factories文件、@ConditionalOnProperty注解

借鉴&#xff1a; 这个链接是包含run方法进来debug看整个过程的&#xff0c;建议先看&#xff1a;https://www.cnblogs.com/starsray/p/15580915.html https://blog.csdn.net/fengxiandada/article/details/130080828 Springboot自动装配 1.创建springboot应用 如何创建一个s…

Double 4 VR智能互动教学系统:创新酒店管理专业课堂教学

一、Double 4 VR智能互动教学系统的特点 1、高度仿真&#xff1a;VR技术可以模拟真实的工作环境&#xff0c;让学生身临其境地体验酒店管理的工作流程。 2、互动性强&#xff1a;通过VR设备&#xff0c;学生可以与虚拟环境中的角色进行互动&#xff0c;增强学习的趣味性和参与…