React 钩子 useCallback 与循环内的参数 [英] React hooks useCallback with parameters inside loop

查看:30
本文介绍了React 钩子 useCallback 与循环内的参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

看看下面的示例代码片段,其中我使用 onClick 处理程序创建了 5 个按钮,该处理程序将按钮的编号打印到控制台.

const Example = (props) =>{const onClick = (key) =>{console.log("您点击了:", key);};返回(<div>{_.times(5, (key) => {返回 (<button onClick={() =>onClick(key)}>{钥匙});})}

);};console.log("你好");ReactDOM.render(, 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'>

现在,由于我在 <button onClick={() => 中使用了箭头函数,所以每次示例渲染时它都会创建一个新的函数实例.onClick(key)}>,我这样做是因为我希望 onClick 函数知道哪个按钮调用了它.我想我可以通过使用 useCallback 来使用新的 React 钩子功能来防止这种情况发生,但是如果我将它应用到 const onClick 那么它仍然会在每次渲染时创建一个新实例,因为提供 key 参数所需的内联箭头函数,据我所知,我不允许将它应用于循环内的渲染(特别是如果循环顺序可能会改变?).

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

解决方案

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

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

<小时>

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

然后从单击处理程序内的 event.target.dataset["key"] 中读取键:

const Example = (props) =>{//单个回调,所有按钮共享const onClick = React.useCallback((e) => {//检查哪个按钮被点击const key = e.target.dataset["key"]console.log("您点击了:", key);}, [/* 依赖关系 */]);返回(<div>{_.times(5, (key) => {返回 (<button data-key={key} onClick={onClick}>{钥匙});})}

);};console.log("你好");ReactDOM.render(, 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'>

<小时>

综上所述,可以在一个钩子中记住多个函数.useCallback(fn, deps) 等价于 useMemo(() => fn, deps)useMemo 可以用来记忆多个立即运行:

const clickHandlers = useMemo(() => _.times(5, key =>() =>console.log("你点击了", key)), [/* 依赖关系 */]);

const Example = (props) =>{const clickHandlers = React.useMemo(() => _.times(5, key =>() =>console.log("你点击了", key)), [/* 依赖关系 */])返回(<div>{_.times(5, (key) => {返回 (<button onClick={clickHandlers[key]}>{钥匙});})}

);};console.log("你好");ReactDOM.render(, 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'>

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

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.

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. 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:

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:

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.

这篇关于React 钩子 useCallback 与循环内的参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
其他开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆