Joh*_*ohn 5 optimization reactjs
有一个渲染用户卡的组件
\nimport React from "react";\n\nconst User = React.memo(function({id, name, isSelected, ...other}) {\n return (\n <div {...other}>\n {name} - {isSelected && "Selected"}\n </div>\n );\n});\n\nexport default User;Run Code Online (Sandbox Code Playgroud)\r\n以及渲染用户卡的父组件
\nimport React from "react";\n\nfunction Application() {\n const [users, setUsers] = React.useState([\n {id: 1, name: "John Doe #1"},\n {id: 2, name: "John Doe #2"},\n {id: 3, name: "John Doe #3"}\n ]);\n const [selectedUserId, setSelectedUserId] = React.useState(null);\n\n return users.map((user) => {\n const isSelected = selectedUserId === user.id;\n\n return (\n <User\n {...user}\n key={user.id}\n isSelected={isSelected}\n onClick={() => setSelectedUserId(user.id)}\n />\n );\n });\n}\n\nexport default Application;Run Code Online (Sandbox Code Playgroud)\r\n任务是“选择用户后避免重新渲染其他用户卡”
\n我尝试过使用React.useCallback钩子,这是我的第一个实现
import React from "react";\n\nconst User = React.memo(function({id, name, isSelected, ...other}) {\n return (\n <div {...other}>\n {name} - {isSelected && "Selected"}\n </div>\n );\n});\n\nfunction Application() {\n const [users, setUsers] = React.useState([\n {id: 1, name: "John Doe #1"},\n {id: 2, name: "John Doe #2"},\n {id: 3, name: "John Doe #3"}\n ]);\n const [selectedUserId, setSelectedUserId] = React.useState(null);\n\n const handleSelectUser = React.useCallback((userId) => () => {\n setSelectedUserId(userId);\n }, []);\n\n return users.map((user) => {\n const isSelected = selectedUserId === user.id;\n\n return (\n <User\n {...user}\n key={user.id}\n isSelected={isSelected}\n onClick={handleSelectUser(user.id)}\n />\n );\n });\n}\n\nexport default Application;Run Code Online (Sandbox Code Playgroud)\r\n在这种情况下,React.useCallback返回一个具有新引用的匿名函数
结果:每次点击后所有用户卡仍然重新渲染
\n我决定将这个匿名函数包装在React.useCallback
import React from "react";\n\nconst User = React.memo(function({id, name, isSelected, ...other}) {\n return (\n <div {...other}>\n {name} - {isSelected && "Selected"}\n </div>\n );\n});\n\nfunction Application() {\n const [users, setUsers] = React.useState([\n {id: 1, name: "John Doe #1"},\n {id: 2, name: "John Doe #2"},\n {id: 3, name: "John Doe #3"}\n ]);\n const [selectedUserId, setSelectedUserId] = React.useState(null);\n\n const handleSelectUser = React.useCallback((userId) => {\n return React.useCallback(() => {\n setSelectedUserId(userId);\n }, []);\n }, []);\n\n return users.map((user) => {\n const isSelected = selectedUserId === user.id;\n\n return (\n <User\n {...user}\n key={user.id}\n isSelected={isSelected}\n onClick={handleSelectUser(user.id)}\n />\n );\n });\n}\n\nexport default Application;Run Code Online (Sandbox Code Playgroud)\r\n问题解决了,但是还有一个问题,我做对了吗?React 团队说:Don\xe2\x80\x99t 在循环、条件或嵌套函数内调用 Hooks,我会得到什么副作用?
\nPS 请勿触摸User组件
为什么你不能在钩子内部调用钩子 - 去这里进行超深入的研究,比我在这个答案中需要提供的更多上下文https://overreacted.io/why-do-hooks-rely-on-call-order /
您的解决方案之所以有效,是因为尽管“违反了规则”,但对挂钩的调用顺序始终相同......直到用户被添加或从状态中删除。
您绝对可以按照所写的方式使用您的解决方案。但是,如果您需要更改用户数量,会发生什么情况呢?
不,你不能在钩子内使用钩子。它可能“有效”,但 React 告诉你它无法可靠地工作,并且你做错了。必须在自定义钩子顶层组件的顶层调用钩子。
您走在正确的道路上,但解决问题的方法是
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}
/>
);
});
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
312 次 |
| 最近记录: |