简体   繁体   中英

Infinite loop React hooks

I don't understand why I'm getting infinite loop in useClick I see that I change state value inside useEffect using setVal but useEffect should work only on onClick as specified in second param. I thought that it is because the param onClick i pass is memoized but the callback is not called(i checked that using console.log('go set')

function useClick(onClick, setVal, val) {
  React.useEffect(() => {
    console.log('Click');
    setVal(val + 1);
  }, [onClick]);
}

const Home = () => {
  const [val, setVal] = React.useState(0);
  const incrementOnClick = React.useCallback(() => {
    console.log('go set');
    setVal(val + 1);
  } , [setVal, val]);
  useClick(incrementOnClick, setVal, val);
  return <div>
    <div>{val}</div>
    <button onClick={incrementOnClick}>Click me</button>
 </div>
}

val and setVal will change on every render, which in turn will cause incrementOnClick to become a new function reference, and your useClick effect will always be invoked.

You could instead give a function as first argument to setVal . This function gets the current val as argument and returns the new value. This way incrementOnClick will always be the same function.

 const { useEffect, useState, useCallback } = React; function useClick(onClick, setVal, val) { useEffect(() => { console.log("Click"); setVal(val + 1); }, [onClick]); } const Home = () => { const [val, setVal] = useState(0); const incrementOnClick = useCallback(() => { console.log("go set"); setVal(val => val + 1); }, []); useClick(incrementOnClick, setVal, val); return ( <div> <div>{val}</div> <button onClick={incrementOnClick}>Click me</button> </div> ); }; ReactDOM.render(<Home />, document.getElementById("root")); 
 <script src="https://unpkg.com/react@16/umd/react.development.js"></script> <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script> <div id="root"></div> 

The code above shows how you could get away from the infinite loop and could be valuable for experimentation, but most of it isn't necessary. You could write the same functionality like this instead:

 const { useState } = React; const Home = () => { const [val, setVal] = useState(1); return ( <div> <div>{val}</div> <button onClick={() => setVal(val + 1)}>Click me</button> </div> ); }; ReactDOM.render(<Home />, document.getElementById("root")); 
 <script src="https://unpkg.com/react@16/umd/react.development.js"></script> <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script> <div id="root"></div> 

Looking at what your trying to do, I believe your missing one of the most useful features of Hooks & React, and that's composition.

Here is an example of what you have done, but just creating another component called <IncrementButton/> , to me it just makes the code much easier to understand / debug. Custom hooks are great, but for doing this I believe it's the wrong tool..

 const { useEffect, useState } = React; const IncrementButton = props => { const {val, setVal, children} = props; return <button onClick={() => setVal(val + 1)} >{children}</button>; } const Home = () => { const [val, setVal] = useState(0); return ( <div> <div>{val}</div> <IncrementButton val={val} setVal={setVal}> Click me </IncrementButton> </div> ); }; ReactDOM.render(<Home />, document.getElementById("root")); 
 <script src="https://unpkg.com/react@16/umd/react.development.js"></script> <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script> <div id="root"></div> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM