使用循环内的参数反应钩子useCallback

时间:2019-03-05 15:18:34

标签: reactjs react-hooks

我正在尝试了解钩子功能,但是我似乎还不太清楚如何正确使用功能useCallback。据我从有关钩子的规则了解到,我应该将它们称为顶级,而不是在逻辑(如循环)之内。这让我很困惑,应该如何在从循环渲染的组件上实现useCallback

看看下面的示例代码片段,我在其中创建了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>

现在,由于我在<button onClick={() => onClick(key)}>中使用了箭头功能,因此每次实例渲染时都会创建一个新的函数实例,这是因为我希望onClick函数知道哪个按钮调用了它。我以为我可以使用useCallback来阻止新的react hooks功能,但是如果我将其应用于const onClick,由于需要使用内联箭头功能,它仍将在每个渲染中创建一个新实例。给定key参数,就我所知,我不允许将其应用于循环中的渲染(尤其是如果循环顺序可能会改变吗?)。

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

1 个答案:

答案 0 :(得分:2)

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

在这种情况下(或大多数情况下,我怀疑),您不会处理优化的组件,因此没有真正的理由来记住像useCallback这样的回调。


但是,假设备忘录很重要,这里的最佳解决方案可能是使用单个功能而不是五个功能:不是每个按钮都由闭包来key的唯一功能,而是可以附加{{ 1}}到元素:

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(() => fn, deps)可用于一次记住多个功能:

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'));

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