简体   繁体   English

React钩子useCallback与循环内的参数

[英]React hooks useCallback with parameters inside loop

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. 我正在尝试了解钩子功能,但我似乎无法弄清楚我应该如何正确使用函数useCallback 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. 这让我很困惑,我应该如何在从循环中呈现的组件上实现useCallback

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. 看一下下面的示例代码片段,其中我创建了5个带有onClick处理程序的按钮,该处理程序将按钮的编号打印到控制台。

 const Example = (props) => { const onClick = (key) => { console.log("You clicked: ", key); }; return( <div> { _.times(5, (key) => { return ( <button onClick={() => onClick(key)}> {key} </button> ); }) } </div> ); }; console.log("hello there"); ReactDOM.render(<Example/>, document.getElementById('root')); 
 <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.11/lodash.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script> <div id='root'> </div> 

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. 由于我在<button onClick={() => onClick(key)}>使用了箭头函数,因此每次示例呈现时它都会创建一个新的函数实例,我这样做是因为我希望onClick函数知道哪个按钮叫它。 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?). 我认为我可以通过使用useCallback来阻止这个新的反应钩子功能,但是如果我将它应用于const onClick那么它仍会在每次渲染时创建一个新实例,因为提供key参数所需的内联箭头功能,我据我所知,不允许将它应用于循环中的渲染(特别是如果循环顺序可能会改变吗?)。

So how would I go about implementing useCallback in this scenario while keeping the same functionality? 那么在保持相同功能的同时,如何在这种情况下实现useCallback呢? Is it at all possible? 它可能吗?

The simple answer here is, you probably shouldn't use useCallback here. 这里简单的答案是,你可能不应该在这里使用useCallback The point of useCallback is to pass the same function instance to optimized components (eg PureComponent or React.memo ized components) to avoid unnecessary rerenders. useCallback是将相同的函数实例传递给优化的组件(例如PureComponentReact.memo组件)以避免不必要的重新渲染。

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 . 在这种情况下你没有处理优化的组件(或者大多数情况下,我怀疑)因此没有理由useCallbackuseCallback这样的回调。


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: 假设记忆很重要,这里最好的解决方案可能是使用单个函数而不是五个:而不是每个按钮的唯一函数通过闭包来携带key ,您可以将key附加到元素:

<button data-key={key}>{key}</button>

And then read the key from the event.target.dataset["key"] inside a single click handler: 然后在单击处理程序中读取event.target.dataset["key"]

 const Example = (props) => { // Single callback, shared by all buttons const onClick = React.useCallback((e) => { // Check which button was clicked const key = e.target.dataset["key"] console.log("You clicked: ", key); }, [/* dependencies */]); return( <div> { _.times(5, (key) => { return ( <button data-key={key} onClick={onClick}> {key} </button> ); }) } </div> ); }; console.log("hello there"); ReactDOM.render(<Example/>, document.getElementById('root')); 
 <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.11/lodash.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script> <div id='root'> </div> 


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: useCallback(fn, deps)等同于useMemo(() => fn, deps)useMemo可用于一次useMemo多个函数:

const clickHandlers = useMemo(() => _.times(5, key => 
  () => console.log("You clicked", key)
), [/* dependencies */]);

 const Example = (props) => { const clickHandlers = React.useMemo(() => _.times(5, key => () => console.log("You clicked", key) ), [/* dependencies */]) return( <div> { _.times(5, (key) => { return ( <button onClick={clickHandlers[key]}> {key} </button> ); }) } </div> ); }; console.log("hello there"); ReactDOM.render(<Example/>, document.getElementById('root')); 
 <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.11/lodash.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script> <div id='root'> </div> 

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. 也许有一种情况,这是有用的,但在这种情况下,我要么不管它(而不是担心优化)或者为每个按钮使用一个处理程序。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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