useState异步回调获取不到最新值 您所在的位置:网站首页 dispatch函数的返回值 useState异步回调获取不到最新值

useState异步回调获取不到最新值

2024-07-16 05:40| 来源: 网络整理| 查看: 265

目录

 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 = 1 setNumber(8) // state - > 8 setNumber((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=2 const [count, setCount] = React.useState(() => { if(props.c < 5) return 0 if(props.c < 10) return 1 return 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 { number } 点击 } // 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 ( change ); } 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 ( change ); // [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 ( change ); } 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. 声明一个refArr useEffect(() => { 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 ( {arr.toString()} change ); } 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 ( {arr.toString()} change ); }


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有