Search code examples
javascriptreactjsonclickbindusecallback

how to write onClick (with arguments) optimized by useCallback in React?


I need to implement a long list, each item in the long list will trigger a new function when onClick, because this function is unchanged every time it is rendered, so I want to use useCallback to optimize it, this returned function Fn needs to pass in parameters, so should I use bind to pass parameters in onClick?

    const func = useCallback((num) => setIndex(num), [])
    // myComponent
    <TableItem onClick = { func.bind(null, index) } />

This is my first question, please forgive me if something is wrong,Thanks.


Solution

  • If you pass a callback to multiple components then you can use useCallback in the following way:

    //make Counter a pure component (only re renders if something changed)
    const Counter = React.memo(function Counter({
      counter,
      up,
      index,
    }) {
      // ok to do onClick={new=>'function'} because we do not pass
      // onClick to sub components unless counter changes
      console.log('rendering index:', index);
      return (
        <button onClick={() => up(index)}>{counter}</button>
      );
    });
    const App = () => {
      //fixed amount of items that does not re order so can use index
      //  in key, do not use index as key when you don't have fixed
      //  amount of items or re order the list.
      const [counters, setCounters] = React.useState([0, 0]);
      const up = React.useCallback(
        (index) =>
          //pass callback to setCounters so counters is not
          //  a dependency of useCallback
          setCounters((counters) =>
            counters.map((counter, i) =>
              i === index ? counter + 1 : counter
            )
          ),
        []//empty dependency, only created when App mounts
      );
      return (
        <ul>
          {counters.map((counter, index) => (
            <Counter
              up={up}
              counter={counter}
              key={index}
              index={index}
            />
          ))}
        </ul>
      );
    };
    ReactDOM.render(<App />, document.getElementById('root'));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
    <div id="root"></div>