删除组件后如何清理react js中的useEffect函数
Posted
技术标签:
【中文标题】删除组件后如何清理react js中的useEffect函数【英文标题】:how to cleanup the useEffect function in react js after removing component 【发布时间】:2021-12-23 15:47:19 【问题描述】:在 react js 中使用 useRef 和 useEffect 时出现此错误。 **如何清理 React js 中的 useEffect 这是所有问题的主题** Dropdown.js:9
Uncaught TypeError: Cannot read properties of null (reading 'contains')
at htmlDocument.bodydroptoggler (Dropdown.js:9)
这里是截图:
当我点击名为“drop toggler”的按钮时出现此错误
这里是 app.js 的代码
import React, useState from "react";
import Dropdown from "./components/Dropdown";
const options = [
label: "red color is selected",
value: "red",
,
label: "blue color is selected",
value: "blue",
,
label: "green color is seleted",
value: "green",
,
];
const App = () =>
const [dropactive, setDropactive] = useState(true);
return (
<div className="container ui">
<button
className="button ui"
onClick=() => setDropactive(!dropactive)
>
drop toggler
</button>
dropactive ? <Dropdown options=options /> : null
</div>
);
;
export default App;
这里是 dropdown.js 的代码
import React, useState, useRef, useEffect from "react";
const Dropdown = ( options ) =>
const [selected, setSelected] = useState(options[0]);
const [open, setOpen] = useState(false);
const ref = useRef();
useEffect(() =>
const bodydroptoggler = (event) =>
if (ref.current.contains(event.target))
return;
setOpen(false);
;
document.addEventListener("click", bodydroptoggler);
return () =>
document.removeEventListener("click", bodydroptoggler);
console.log("work");
;
, []);
const RenderedOptions = options.map((option, index) =>
if (selected.value === option.value)
return null;
else
return (
<div
className="item"
key=index
onClick=() =>
setSelected(option);
>
option.label
</div>
);
);
return (
<div ref=ref className="ui form">
<div className="field">
<label className="text label">Select from here:</label>
<div
className=`ui selection dropdown $
open ? "active visible" : ""
`
onClick=() => setOpen(!open)
>
<i className="dropdown icon"></i>
<div className="text">selected.label</div>
<div className=`menu $open ? "visible transition" : ""`>
RenderedOptions
</div>
</div>
</div>
</div>
);
;
export default Dropdown;
这是我想要执行的 我只想通过单击按钮来隐藏该表单。
如何运行这个项目
-
只需创建一个反应应用程序
将 app.js 的代码放到你项目的 app.js 中
组件文件夹内的dropdown.js
我希望这些详细信息对您有所帮助,如果您需要更多信息,请联系我们 提前致谢
【问题讨论】:
【参考方案1】:您是否尝试过使用可选链接,因为 ref.current 有时可能未定义?
if (ref.current?.contains(event.target))
这是一个带有修复程序的codesandbox link。
还有来自React Ref docs 的一些附加上下文,说明为什么有时 ref 可能为空
React 将在组件挂载时将当前属性分配给 DOM 元素,并在卸载时将其分配回 null。
【讨论】:
【参考方案2】:编辑:
这就是useLayoutEffect
的用途。它同步运行它的内容(和清理)并避免竞争条件。这是证明这一点的 stackblitz:
https://stackblitz.com/edit/react-5w7vog
查看来自 Kent Dodd's 的 this post:
您可能希望使用 useLayoutEffect 而不是 useEffect 的另一种情况是,如果您正在更新一个值(如 ref),并且您希望在任何其他代码运行之前确保它是最新的。例如:
原始答案
这很复杂。您的代码通常看起来不错,所以我花了一分钟才明白为什么。但这是为什么 - Dropdown
组件在运行效果清理之前卸载。所以 click 事件仍然会找到处理程序,这次对 ref 有一个空引用(因为 ref 会立即更新)。
您的代码是正确的、惯用的 React - 但这是一个需要更深入理解的边缘案例。
正如其他回答者已经提到的,只需添加一个可选检查。但我想你可能想知道为什么。
【讨论】:
如果useEffect
的清理函数确实在组件卸载后运行(我相信确实如此),那么 OP 代码怎么可能也是正确的、惯用的 React 代码?
因为无法保证何时调用清理函数。不过,我不确定这个问题是否可以通过 useLayoutEffect 解决。如果这能解决它,你可能是正确的
我想如果你只是对你的评论很迂腐,那么是的,OP 错过了 ref.current 的可选选项。但是,我的意思是他们正确地清理了一个效果
我不想对评论变得迂腐。我试图确定您关于 OP 代码是惯用的说法是否具有误导性。 IMO,这不是惯用的,因为我总是看到 React refs 在所有情况下都被视为可以为空。话虽如此,我主要是用类型语言编写 React,并且类型系统强迫我在所有情况下都将 ref 视为可为空的。
第一个答案非常适合我。但我想澄清我对 react js cleanup 的概念。有什么办法可以借助清理方法解决这个问题。以上是关于删除组件后如何清理react js中的useEffect函数的主要内容,如果未能解决你的问题,请参考以下文章