删除组件后如何清理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函数的主要内容,如果未能解决你的问题,请参考以下文章

如何取消firebase的useEffect订阅

如何在头组件中使用效果(ReactJS/Next.js)

React:如何使用功能性 usestate/useEffect 复制特定的组件类 setstate 回调示例?

react.js map遍历的问题

React 中带有 useEffect 的异步上下文

测量你的组件在 React Native 功能组件中渲染的次数