我可以在另一个 React.useCallback 中使用 React.useCallback 吗?
P粉153503989
P粉153503989 2023-09-01 14:55:58
[React讨论组]
<p>有一个渲染用户卡片的组件</p> <p> <pre class="brush:js;toolbar:false;">import React from "react"; const User = React.memo(function({id, name, isSelected, ...other}) { return ( &lt;div {...other}&gt; {name} - {isSelected &amp;&amp; "Selected"} &lt;/div&gt; ); }); export default User;</pre> </p> <p>以及渲染用户卡的父组件</p> <p> <pre class="brush:js;toolbar:false;">import React from "react"; function Application() { const [users, setUsers] = React.useState([ {id: 1, name: "John Doe #1"}, {id: 2, name: "John Doe #2"}, {id: 3, name: "John Doe #3"} ]); const [selectedUserId, setSelectedUserId] = React.useState(null); return users.map((user) =&gt; { const isSelected = selectedUserId === user.id; return ( &lt;User {...user} key={user.id} isSelected={isSelected} onClick={() =&gt; setSelectedUserId(user.id)} /&gt; ); }); } export default Application;</pre> </p> <p>任务是“选择用户后避免重新渲染其他用户卡”</p> <p>我尝试使用 <code>React.useCallback</code> 钩子,这是我的第一个实现</p> <p> <pre class="brush:js;toolbar:false;">import React from "react"; const User = React.memo(function({id, name, isSelected, ...other}) { return ( &lt;div {...other}&gt; {name} - {isSelected &amp;&amp; "Selected"} &lt;/div&gt; ); }); function Application() { const [users, setUsers] = React.useState([ {id: 1, name: "John Doe #1"}, {id: 2, name: "John Doe #2"}, {id: 3, name: "John Doe #3"} ]); const [selectedUserId, setSelectedUserId] = React.useState(null); const handleSelectUser = React.useCallback((userId) =&gt; () =&gt; { setSelectedUserId(userId); }, []); return users.map((user) =&gt; { const isSelected = selectedUserId === user.id; return ( &lt;User {...user} key={user.id} isSelected={isSelected} onClick={handleSelectUser(user.id)} /&gt; ); }); } export default Application;</pre> </p> <p>在这种情况下,<code>React.useCallback</code> 返回一个具有新引用的匿名函数</p> <p><strong>结果:每次点击后所有用户卡仍重新呈现</strong></p> <p>我决定将这个匿名函数包装在 <code>React.useCallback</code></p> <p> <pre class="brush:js;toolbar:false;">import React from "react"; const User = React.memo(function({id, name, isSelected, ...other}) { return ( &lt;div {...other}&gt; {name} - {isSelected &amp;&amp; "Selected"} &lt;/div&gt; ); }); function Application() { const [users, setUsers] = React.useState([ {id: 1, name: "John Doe #1"}, {id: 2, name: "John Doe #2"}, {id: 3, name: "John Doe #3"} ]); const [selectedUserId, setSelectedUserId] = React.useState(null); const handleSelectUser = React.useCallback((userId) =&gt; { return React.useCallback(() =&gt; { setSelectedUserId(userId); }, []); }, []); return users.map((user) =&gt; { const isSelected = selectedUserId === user.id; return ( &lt;User {...user} key={user.id} isSelected={isSelected} onClick={handleSelectUser(user.id)} /&gt; ); }); } export default Application;</pre> </p> <p>问题解决了,但是还有一个问题,我做对了吗? React 团队说:<em>不要在循环、条件或嵌套函数内调用 Hooks</em>,我会得到什么副作用?</p> <p>附注不要触摸<code>用户</code>组件</p>
P粉153503989
P粉153503989

全部回复(1)
P粉810050669

解释为什么不能在钩子内部调用钩子(并期望它一致且可靠地工作)

为什么你不能在钩子内部调用钩子 - 去这里进行超深入的研究,其中的上下文比我在这个答案中需要提供的更多https://overreacted.io/why-do-hooks-rely-on-call-order/

您的解决方案之所以有效,是因为尽管“违反了规则”,但对挂钩的调用顺序始终相同......直到用户被添加或从状态中删除。

您绝对可以使用您所编写的解决方案。但是,在您需要 更改用户数量


不,你不能在钩子内使用钩子。它可能“有效”,但 React 告诉你它无法可靠地工作,并且你做错了。必须在自定义钩子顶层组件的顶层调用钩子。

您的方向是正确的,但解决问题的方法是

  1. 使用提供给回调函数的参数作为被单击元素的源 AND
  2. 如果您无法修改 User 组件,则必须在 div 元素上提供一些数据,以便让您知道点击了哪个用户元素。

它看起来像这样:

function Application() {
  const [users, setUsers] = React.useState([
    { id: 1, name: 'John Doe #1' },
    { id: 2, name: 'John Doe #2' },
    { id: 3, name: 'John Doe #3' },
  ]);
  const [selectedUserId, setSelectedUserId] = React.useState(null);

  // this callback is referentially stable - it doesn't change between renders because it has no dependencies
  const handleSelectUser = React.useCallback((e) => {
    setSelectedUserId(+e.target.getAttribute('data-userid'));
  }, []);

  return users.map((user) => {
    const isSelected = selectedUserId === user.id;

    return (
      <User
        {...user}
        data-userid={user.id} <- this line
        key={user.id}
        isSelected={isSelected}
        onClick={handleSelectUser}
      />
    );
  });
}
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习
PHP中文网抖音号
发现有趣的

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号