javascript - React useCallback with debounce 适用于旧值,如何获得实际状态值?

标签 javascript reactjs use-effect usecallback

我不能满足所有条件:

  • 我需要一些函数在 useCallback ,因为我将它设置为子组件的 Prop (用于防止重新渲染)
  • 我需要使用 debounce ,因为我的函数是“终点”,可以调用~100次/秒
  • 我需要在去抖后获取当前(实际值)。

  • 我对最后一点有疑问,我在去抖动(1000 毫秒)后的值已经过时了。

    如何使用 useCallback 获取当前值+ debounce ? (警报中的值必须与页面相同)

    enter image description here

    //ES6 const, let
    //ES6 Destructuring 
    const { Component, useCallback, useState, useEffect } = React;
    
    const SUBChildComponent = (props) => (<button onClick={props.getVal}>GetValue with debounce</button>);
    
    const ChildComponent = () => {
        // some unstable states
        const [someVal1, setSomeVal1] = useState(0);
        const [someVal2, setSomeVal2] = useState(0);
        const [someVal3, setSomeVal3] = useState(0);
    
        // some callback witch works with states AND called from subClild components
        const getVal = useCallback(_.debounce(() => {
            alert(`${someVal1}\n${someVal2}\n${someVal3}`);
        }, 1000), [someVal1, someVal2, someVal3]);
    
        // some synthetic changes
        useEffect(() => {
            const id = setInterval(() => setSomeVal1(someVal1 + 1), 50);
            return () => clearInterval(id);
        }, [someVal1]);
    
        // some synthetic changes
        useEffect(() => {
            const id = setInterval(() => setSomeVal2(someVal2 + 1), 100);
            return () => clearInterval(id);
        }, [someVal2]);
    
        // some synthetic changes
        useEffect(() => {
            const id = setInterval(() => setSomeVal3(someVal3 + 1), 250);
            return () => clearInterval(id);
        }, [someVal3]);
    
        return <React.Fragment><SUBChildComponent getVal={getVal}/><br/>{someVal1}<br/>{someVal2}<br/>{someVal3}
        </React.Fragment>;
    };
    
    class App extends Component {
        render() {
            return (<div><ChildComponent/></div>);
        }
    }
    
    ReactDOM.render(<App/>, document.querySelector(".container"));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.5.0/lodash.min.js"></script>
    
    <div class="container"></div>

    最佳答案

    首先,您必须注意 debounce 函数在创建时设置其关闭状态。现在该函数在几秒钟后执行,到那时状态已经改变。此外,每次更新状态时都会创建一个新的去抖动实例,因此如果您完全使用去抖动函数 onClick,它将无法正常工作,因为不同的调用将调用不同的去抖动函数实例而不是同一个

    在这种情况下,解决方案是将状态值作为参数传递给 debounce 函数,而不是让它依赖于闭包。但是,它仍然使用调用 debounce 的值,如下面的代码片段所示

    //ES6 const, let
    //ES6 Destructuring 
    const { Component, useCallback, useState, useEffect } = React;
    
    const SUBChildComponent = ({getVal, someVal1,someVal2,someVal3}) => (<button onClick={() => getVal(someVal1,someVal2,someVal3)}>GetValue with debounce</button>);
    
    const ChildComponent = () => {
        // some unstable states
        const [someVal1, setSomeVal1] = useState(0);
        const [someVal2, setSomeVal2] = useState(0);
        const [someVal3, setSomeVal3] = useState(0);
    
        // some callback witch works with states AND called from subClild components
        const getVal = useCallback(_.debounce((val1, val2, val3) => {
            alert(`${val1}\n${val2}\n${val3}`);
        }, 1000), []); // create debounce function only once
    
        // some synthetic changes
        useEffect(() => {
            const id = setInterval(() => setSomeVal1(someVal1 + 1), 50);
            return () => clearInterval(id);
        }, [someVal1]);
    
        // some synthetic changes
        useEffect(() => {
            const id = setInterval(() => setSomeVal2(someVal2 + 1), 100);
            return () => clearInterval(id);
        }, [someVal2]);
    
        // some synthetic changes
        useEffect(() => {
            const id = setInterval(() => setSomeVal3(someVal3 + 1), 250);
            return () => clearInterval(id);
        }, [someVal3]);
    
        return <React.Fragment><SUBChildComponent someVal1={someVal1} someVal2={someVal2} someVal3={someVal3} getVal={getVal}/><br/>{someVal1}<br/>{someVal2}<br/>{someVal3}
        </React.Fragment>;
    };
    
    class App extends Component {
        render() {
            return (<div><ChildComponent/></div>);
        }
    }
    
    ReactDOM.render(<App/>, document.querySelector(".container"));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.5.0/lodash.min.js"></script>
    
    <div class="container"></div>


    现在另一种解决方案是保留状态的引用并在 debounce 函数中使用它们,这就是你在你的情况下想要的

    //ES6 const, let
    //ES6 Destructuring 
    const { Component, useCallback, useState, useEffect, useRef } = React;
    
    const SUBChildComponent = React.memo(({getVal}) => {
        console.log('child render');
        return <button onClick={() => getVal()}>GetValue with debounce</button>;
    });
    
    const ChildComponent = () => {
        // some unstable states
        const [someVal1, setSomeVal1] = useState(0);
        const [someVal2, setSomeVal2] = useState(0);
        const [someVal3, setSomeVal3] = useState(0);
        const someVal1Ref = useRef(someVal1);
        const someVal2Ref = useRef(someVal2);
        const someVal3Ref = useRef(someVal3);
         
        useEffect(() => {
            someVal1Ref.current = someVal1;
            someVal2Ref.current = someVal2;
            someVal3Ref.current = someVal3;
        }, [someVal1, someVal2, someVal3])
        
        // some callback witch works with states AND called from subClild components
        const getVal = useCallback(_.debounce(() => {
            alert(`${someVal1Ref.current}\n${someVal2Ref.current}\n${someVal3Ref.current}`);
        }, 1000), []); // create debounce function only once
    
        // some synthetic changes
        useEffect(() => {
            const id = setInterval(() => setSomeVal1(someVal1 + 1), 50);
            return () => clearInterval(id);
        }, [someVal1]);
    
        // some synthetic changes
        useEffect(() => {
            const id = setInterval(() => setSomeVal2(someVal2 + 1), 100);
            return () => clearInterval(id);
        }, [someVal2]);
    
        // some synthetic changes
        useEffect(() => {
            const id = setInterval(() => setSomeVal3(someVal3 + 1), 250);
            return () => clearInterval(id);
        }, [someVal3]);
    
        return <React.Fragment><SUBChildComponent getVal={getVal}/><br/>{someVal1}<br/>{someVal2}<br/>{someVal3}
        </React.Fragment>;
    };
    
    class App extends Component {
        render() {
            return (<div><ChildComponent/></div>);
        }
    }
    
    ReactDOM.render(<App/>, document.querySelector(".container"));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.5.0/lodash.min.js"></script>
    
    <div class="container"></div>


    PS。 这种实现在类组件中更容易,并且不需要任何工作,因为您不依赖于闭包

    关于javascript - React useCallback with debounce 适用于旧值,如何获得实际状态值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61886958/

    相关文章:

    Javascript 通过单选按钮名称获取值

    javascript - 更改超链接的点击

    reactjs - React Hooks : How to useRef(). 电流正确且干净

    javascript - 组件本身是否应该防止不必要的 useEffect() 调用?

    reactjs - 为什么在 useEffect 的依赖数组中需要 history

    javascript - Node.js URL POST 重写

    javascript - Angular 如何重新加载 Controller

    javascript - 从功能组件发送功能 Prop 时防止重新渲染

    reactjs - Material -ui : Table scroll to Top of new page

    reactjs - 即使我没有在 React 中单击“加载更多”,也要保持数据添加