反应钩子 useCallback 与循环内的参数

Posted

技术标签:

【中文标题】反应钩子 useCallback 与循环内的参数【英文标题】:React hooks useCallback with parameters inside loop 【发布时间】:2019-07-27 02:41:59 【问题描述】:

我正在尝试了解挂钩功能,但我似乎不太明白应该如何正确使用函数useCallback。据我从有关钩子的规则中了解,我应该将它们称为***而不是逻辑(例如循环)。这让我很困惑我应该如何在从循环呈现的组件上实现useCallback

请看下面的示例 sn-p,其中我使用 onClick 处理程序创建了 5 个按钮,该处理程序将按钮的编号打印到控制台。

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>

现在,由于我在&lt;button onClick=() =&gt; onClick(key)&gt; 中使用箭头函数,因此每次呈现示例时它都会创建一个新函数实例,我这样做是因为我希望onClick 函数知道哪个按钮调用了它。我想我可以通过使用useCallback 使用新的反应钩子功能来防止这种情况,但是如果我将它应用于const onClick 那么它仍然会在每次渲染时创建一个新实例,因为内联箭头函数需要给@987654330 @ 参数,据我所知,我不允许将它应用于循环内的渲染(特别是如果循环顺序可能会改变吗?)。

那么我将如何在这种情况下实现useCallback,同时保持相同的功能?有可能吗?

【问题讨论】:

【参考方案1】:

这里的简单答案是,您可能不应该在这里使用useCallbackuseCallback 的重点是将相同的函数实例传递给优化的组件(例如 PureComponentReact.memoized 组件)以避免不必要的重新渲染。

在这种情况下(或者大多数情况下,我怀疑)你没有处理优化的组件,所以没有理由像 useCallback 那样记住回调。


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

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

然后在单击处理程序中从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>

话虽如此,在一个钩子中记忆多个函数是可能的。 useCallback(fn, deps)等价于useMemo(() =&gt; fn, deps)useMemo可以一次memoize多个函数:

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>

也许在某些情况下这很有用,但在这种情况下,我要么不理会它(不用担心优化),要么为每个按钮使用一个处理程序。

【讨论】:

这个答案的data-*属性部分也在React documentation中提供,用于优化大量元素的回调事件 OP 没有就何时适合使用回调征求意见。这是没有答案的。 @tshm001 他们询问“正确使用”,有时正确使用是不使用它。我确实展示了如何记忆一组回调(useMemouseCallback 的目的相同)。如果您有更好的答案,请随时发布。

以上是关于反应钩子 useCallback 与循环内的参数的主要内容,如果未能解决你的问题,请参考以下文章

useMemo和useCallback:何时使用它们,何时不使用它们

useMemo和useCallback:何时使用它们,何时不使用它们

useMemo和useCallback:何时使用它们,何时不使用它们

useMemo和useCallback:何时使用它们,何时不使用它们

具有依赖关系的 useCallback 与使用 ref 调用函数的最后一个版本

React Hooks(钩子函数)