I'm trying to learn about the hooks functionality, however I can't quite seem to figure out how I'm supposed to use the function useCallback properly. As far as I understand from the rules about hooks I'm supposed to call them top-level and not within logic (such as loops). Which leads me to be quite confused how I'm supposed to implement useCallback on components that are rendered from a loop.
Take a look at the following example snippet where I create 5 buttons with an onClick handler that prints the number of the button to the console.
Right now it creates a new function instance everytime the Example renders due to my use of the arrow function within the <button onClick={() => onClick(key)}>, which I do because I want the onClick function to know which button called it. I thought I could prevent this with the new react hooks functionality by using useCallback, however if I apply it to the const onClick then it would still create a new instance every render because of the inline arrow function needed to give the key parameter, and I'm not allowed to apply it to the render within a loop as far as I know (especially if the loop order might change right?).
So how would I go about implementing useCallback in this scenario while keeping the same functionality? Is it at all possible?
解决方案
The simple answer here is, you probably shouldn't use useCallback here. The point of useCallback is to pass the same function instance to optimized components (e.g. PureComponent or React.memoized components) to avoid unnecessary rerenders.
You're not dealing with optimized components in this case (or most cases, I'd suspect) so there's not really a reason to memoize callbacks like with useCallback.
Supposing the memoization is important, though, the best solution here is probably to use a single function instead of five: instead of a unique function for each button carries the key by closure, you can attach the key to the element:
<button data-key={key}>{key}</button>
And then read the key from the event.target.dataset["key"] inside a single click handler:
All that being said, it is possible to memoize multiple functions in a single hook. useCallback(fn, deps) is equivalent to useMemo(() => fn, deps), and useMemo can be used to memoize multiple functions at once:
Perhaps there's a case where that's useful, but in this case I would either leave it alone (and not worry about the optimization) or else use a single handler for each button.