Nul*_*ter 5 javascript reactjs react-hooks use-effect
我已经使用 hooks 一段时间了,但我从来没有完全理解为什么 React 强迫我在 useEffect 上包含一些我不想要的依赖项。
\n我理解 useEffect 挂钩的“依赖关系”的方式
\n添加您想要在更改时“监听”的值并触发您的效果。这与简单的效果完美配合,例如:
\nimport React, {useEffect, useState} from "react";\n\ninterface Props {\n\xc2\xa0 \xc2\xa0 id: string\n}\n\nconst SimpleComponent = (props: Props) => {\n\xc2\xa0 \xc2\xa0 const {id} = props;\n\xc2\xa0 \xc2\xa0 const [response, setResponse] = useState<object>();\n\xc2\xa0 \xc2\xa0 \n\xc2\xa0 \xc2\xa0 useEffect(() => {\n\xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 fetch(`https://myexample/${id}`)\n\xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 .then(response => setResponse(response))\n\xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 .catch(() => console.log("An error occurs!"))\n\xc2\xa0 \xc2\xa0 }, [id])\n\xc2\xa0 \xc2\xa0 \n\xc2\xa0 \xc2\xa0 return <div/>\n};\nRun Code Online (Sandbox Code Playgroud)\n然而,还有一些其他情况并不像上面的例子那么简单。在这个例子中我们希望仅当 id 改变时触发效果:
\nimport React, {useEffect} from "react";\n\ninterface Props {\n id: string\n\xc2\xa0 \xc2\xa0 callback: Function\n}\n\nconst SimpleComponent = (props: Props) => {\n\xc2\xa0 \xc2\xa0 const {id, callback} = props;\n\xc2\xa0 \xc2\xa0 \n\xc2\xa0 \xc2\xa0 useEffect(() => {\n\xc2\xa0 \xc2\xa0 \xc2\xa0 \xc2\xa0 callback(id)\n\xc2\xa0 \xc2\xa0 }, [id]);\n\n\xc2\xa0 \xc2\xa0 return <div/>\n};\nRun Code Online (Sandbox Code Playgroud)\n在此示例中,我收到警告“React Hook useEffect 缺少依赖项”,它建议在依赖项数组(选项 1)上包含“回调”或删除依赖项数组(选项 2)。
\n让我们探讨一下建议:
\n选项 1(在依赖项数组上包含“回调”): \n在依赖项数组上包含“回调”将导致每当“id”或“回调”更改时触发我的效果。这样做的问题是,我不想在“回调”更改时触发效果,导致回调在每次渲染中都会更改。
\n选项 2(删除依赖项数组): \n删除依赖项数组将导致每当组件更改时触发我的效果,这也不是所需的行为。
\n我从社区找到了一些其他建议,但所有这些建议似乎都没有实现所需的行为。(/sf/answers/4222952541/)
\n让我们快速回顾一下这些选项:
\n选项 1:使用空依赖项数组:
\n它只会在组件安装时触发,而不是我们想要的。
\n选项 2:在 useEffect() 内声明函数
\n在本例中,“回调”是通过 props 传递的函数,但无论哪种方式,大多数时候您都无法在效果中声明该函数,因为该函数在其他地方使用。
\n选项 3:使用 useCallback() 进行记忆
\n如果将函数包装在 useCallback 中,则还需要将依赖项包含到 useCallback 依赖项数组中,这将导致每次依赖项更改时 useCallback 都会再次触发,因此 useEffect 也会被触发。
\n选项 4:禁用 eslint 的警告
\n没有考虑,因为我试图理解这个问题,而不是简单地忽略它。
\n我对这个警告真的很困惑,我不知道在某些情况下警告是错误的并且应该被忽略(似乎是错误的)或者我错过了一些东西。
\n我个人总是禁用 eslint 规则。由于 eslint 无法理解你的逻辑意图,它只能详尽地检查闭包中捕获的所有变量,并警告你 dep-list 中缺少的变量。但很多时候它是矫枉过正的,就像在你的用例中一样。这就是我选择背后的原因。
如果您清楚地了解其useEffect工作原理,禁用此规则不会造成太大痛苦。我个人不记得经历过它。
第二种解决方案是保留该规则,但绕过它。我给你准备了一个,useFn定制的钩子:
function useFn(fn) {
const ref = useRef(fn);
ref.current = fn;
function wrapper() {
return ref.current.apply(this, arguments)
}
return useRef(wrapper).current
}
Run Code Online (Sandbox Code Playgroud)
这个钩子返回一个稳定的wrapper函数引用,它只是一个调用实际函数的代理fn,但在重新渲染时不会改变。
const SimpleComponent = (props: Props) => {
const {id, callback: _callback} = props;
const callback = useFn(_callback)
useEffect(() => {
callback(id)
}, [id, callback]);
return <div/>
};
Run Code Online (Sandbox Code Playgroud)
现在您满足了 eslint 规则,同时您不会触发不需要的useEffect重新运行。
作为题外话。我还使用useFn钩子来包装传递给子组件的 props 的函数。
传递箭头函数是 React 中大量使用的模式。有时,您有一个重新渲染成本高昂的组件,您React.memo(Component)将其包装起来,然后传递一个<Component onClick={e => { ... }} />内联函数,这实际上使记忆效果无效。useFn前来救援:
const SimpleComponent = (props: Props) => {
const {id, callback: _callback} = props;
const callback = useFn(_callback)
useEffect(() => {
callback(id)
}, [id, callback]);
return <div/>
};
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
927 次 |
| 最近记录: |