【问题标题】:Exponential re-rendering with eventListener on state update在状态更新时使用 eventListener 进行指数重新渲染
【发布时间】:2021-07-21 19:22:57
【问题描述】:

我正在尝试在“keydown”上使用eventListener 来更新我的状态(一个名为showMenu 的布尔值)。 我把它放在useEffect 上,但它不能正常工作,我不知道为什么...... 如果我将 showMenu 放在我的 useEffect 末尾的数组上,它会以指数方式重新渲染组件。 如果我不将showMenu 放在useEffect 的末尾,showMenu 只会更新一次(从 false 到 true)

这是以指数方式重新渲染的代码

const [showMenu, setShowMenu] = useState(false);


useEffect(() => {
    window.addEventListener("keydown", (e) => handleMenu(e));
    return () => {
        window.removeEventListener("keydown", (e) => handleMenu(e));
    };
}, [showMenu]);

const handleMenu = (e) => {
    if (e.key == "m") {
        setShowMenu(!showMenu);
    }
};

【问题讨论】:

    标签: javascript reactjs dom dom-events


    【解决方案1】:

    removeEventListener 接受一个函数 reference 但您每次都在创建一个匿名函数:

    useEffect(() => {
        window.addEventListener("keydown", handleMenu);
        return () => {
            window.removeEventListener("keydown", handleMenu);
        };
    }, [showMenu]);
    

    通过移除匿名函数包装器(又名 eta 缩减)addEventListener 和 removeEventListener 获得相同的函数引用。

    或者如果你只支持现代浏览器并且想忽略函数引用的概念 addEventListener 支持AbortSignals:

    useEffect(() => {
        const ac = new AbortController();
        window.addEventListener("keydown", (e) => handleMenu(e), { signal: ac.signal });
        return () => ac.abort();
    }, [showMenu]);
    

    作为旁注,确实没有理由将showMenu 传递到函数的依赖项列表中,我不会传递它。

    【讨论】:

    • 对不起,我不明白。我是否需要将 handleMenu 放在 useEffect 的末尾?如果这就是你的意思,我仍然有一个指数渲染......也许我需要使用另一个钩子?
    • @feyndev 经过三思而后行(添加了评论)我不会将任何东西作为依赖项列表传递给useEffect - 该列表是“只要依赖项发生变化就运行这个效果” - 因为你的效果不是t 有状态的(它只是附加一个侦听器并且状态在应用程序中)只有在组件卸载时才发生变化时设置侦听器没有真正意义。
    • 但是当按下“m”键时不会调用该函数。状态只改变一次。如何在不使用 useEffect 的情况下每次键入密钥时更新我的​​状态?
    • 在执行诸如手动编辑 DOM 之类的副作用时,您需要使用 useEffect。我认为您必须将 showMenu 传递给依赖项列表,因为函数引用及其值发生了变化。有点不幸,我会更新答案。
    • 我理解你的回答,我在下面发布了工作代码。感谢您抽出宝贵时间!我很感激它,伙计:)
    【解决方案2】:

    好的,刚刚想通了。 useEffect 方法不是问题。逻辑很好。 问题在于(e) => handleMenu(e),我需要直接更改为handleMenu。 见下文:

    const [showMenu, setShowMenu] = useState(false);
    
    const handleMenu = (e) => {
        if (e.key == "m") {
            setShowMenu(!showMenu);
        }
    };
    
    useEffect(() => {
        window.addEventListener("keydown", handleMenu);
        return () => {
            window.removeEventListener("keydown", handleMenu);
        };
    }, [showMenu]);
    

    感谢 Benjamin 给我答案:我需要传递我的 handleMenu 函数引用而不是使用 (e) => 语法,它每次都会根据我的 handleMenu 函数创建一个新函数,它解释了指数渲染:)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-10-24
      • 1970-01-01
      • 2021-01-27
      • 2018-06-05
      • 2023-03-09
      • 1970-01-01
      • 1970-01-01
      • 2019-09-20
      相关资源
      最近更新 更多