简体   繁体   English

在使用事件侦听器和 useEffect 中的状态时缺少依赖项警告

[英]missing dependency warning when working with event listeners and states inside useEffect

Everytime I work with addEventListener() , and also want to access some state inside useEffect , I get the same issue.每次我使用addEventListener()并且还想在 useEffect 访问一些useEffect ,我都会遇到同样的问题。 I can't add the state as dependency, because then I would create multiple event listeners each time the state changes.我无法将 state 添加为依赖项,因为这样每次 state 更改时我都会创建多个事件侦听器。 I almost everytime find myself stuck with the " React Hook useEffect has a missing dependency " warning.我几乎每次都发现自己被“ React Hook useEffect has a missing dependency ”警告卡住了。 Let's say I have a component that needs to change it state on window.onClick() and on window.onDoubleClick() .假设我有一个组件需要在window.onClick()window.onDoubleClick()上更改它 state 。 If the state is true, click should change it to false, and if the state is false, double click should change it to true.如果 state 为真,单击应将其更改为假,如果 state 为假,双击应将其更改为真。 So here's what I whould write:所以这就是我要写的:

import React, { useState, useEffect } from 'react';

export default function someComponent() {
  const [toggle, setToggle] = useState(false);

  useEffect(() => {
    window.addEventListener('click', (event) => {
      if (toggle) setToggle(false)
    })

    window.addEventListener('dblclick', (event) => {
      if (!toggle) setToggle(true)
    })
  }, [])

  return (
    <p>The toggle state is {toggle.toString()}</p>
  );
}

This code works, but I get the missing dependency warning.此代码有效,但我收到缺少依赖项警告。 I can't add toggle to the dependency array, because then it will add another event listener each time the toggle state changes.我无法将切换添加到依赖数组,因为每次toggle state 更改时,它都会添加另一个事件侦听器。

What am I doing wrong here?我在这里做错了什么? how should I fix this?我应该如何解决这个问题?

Edit: Maybe this example wasn't too good, but it's the simplest I could think of.编辑:也许这个例子不太好,但它是我能想到的最简单的。 But, this issue is also for when I create other event listeners, that have to be on the windows object, like scroll.但是,这个问题也适用于当我创建其他事件侦听器时,必须在 windows object 上,比如滚动。 I know I can use return to remove the event listener everytime, but for events like scroll it makes it much slower.我知道我可以每次都使用 return 来删除事件侦听器,但是对于像滚动这样的事件,它会慢得多。 It doesn't make sense to me that I have to remove and add it everytime, when I just don't need it to fire again.每次我都必须删除并添加它对我来说没有意义,因为我只是不需要它再次触发。

With react you don't have to use the window element in this case.在这种情况下,使用 react 你不必使用 window 元素。 Not even a useEffect.甚至没有使用效果。

By using the useEffect hook you are telling react to do something after render (depending on the dependency array).通过使用 useEffect 钩子,你告诉 react 在渲染后做一些事情(取决于依赖数组)。 In this case changing state is not necessary immediately after rendering the page, only when the user interacts with the element.在这种情况下,无需在渲染页面后立即更改 state,只有在用户与元素交互时才需要。

Adding click events through the useEffect is probably not needed most of the time and and doing it like the example below will probably save you time and a headache and maybe even performance (correct me if i'm wrong).大多数时候可能不需要通过 useEffect 添加点击事件,并且像下面的示例那样执行它可能会节省您的时间和头痛,甚至可能会节省性能(如果我错了,请纠正我)。

I would personally do it like this.我个人会这样做。

import React, { useState } from 'react';
        
export default function someComponent() {
  const [toggle, setToggle] = useState(false);

  return (
    <p 
      onClick={() => setToggle(false)} 
      onDoubleClick={() => setToggle(true)}
    >
      The toggle state is {toggle.toString()}
    </p>
  );
}

You could also call functions from the element like so您也可以像这样从元素调用函数

  const [toggle, setToggle] = useState(false);

  const handleClick = () => {
    if (toggle) {
      setToggle(false);
    }
  };

  const handleDoubleClick = () => {
    if (!toggle) {
      setToggle(true);
    }
  };

  return (
    <p 
      onClick={() => handleClick()} 
      onDoubleClick={() => handleDoubleClick()}
    >
      The toggle state is {toggle.toString()}
    </p>
  );

CodeSandbox example代码沙盒示例

编辑 staging-water-cuwo4

You can add a clean-up function to the useEffect hook to remove old listeners.您可以将清理 function 添加到 useEffect 挂钩以删除旧的侦听器。 This way you can pass toggle into the dependency array and you won't have stacking event listeners.通过这种方式,您可以将切换传递给依赖数组,并且您不会有堆叠事件侦听器。

https://reactjs.org/docs/hooks-effect.html https://reactjs.org/docs/hooks-effect.html

 useEffect(() => { const handleClick = () => toggle? setToggle(false): setToggle(true); window.addEventListener('click', handleClick); window.addEventListener('dblclick', handleClick); return () => { window.removeEventListener('click', handleClick); window.removeEventListener('dblclick', handleClick); } }, [toggle]);

I can't add the state as dependency, because then I would create multiple event listeners each time the state changes.我无法将 state 添加为依赖项,因为这样每次 state 更改时我都会创建多个事件侦听器。

There is a way around this, and that is toreturn a cleanup function from the useEffect callback.有一种解决方法,那就是从 useEffect 回调中返回清理 function I would encourage you to read the linked section of the docs, then the below solution would become much clearer:我鼓励您阅读文档的链接部分,然后以下解决方案会变得更加清晰:

useEffect(() => {
  const handleClick = () => {
    setToggle(!toggle)
  }

  window.addEventListener('click', handleClick)    

  return () => {
    window.removeEventListener('click', handleClick)
  }
}, [toggle])

with the above solution, each time toggle is updated, the cleanup function is called, which removes the current event listener before running the effect again.使用上述解决方案,每次更新toggle时,都会调用清理 function,这会在再次运行效果之前删除当前事件侦听器。

Also note that you can provide a callback function to setToggle , which receives the current value of toggle and returns the new value.另请注意,您可以向 setToggle 提供回调setToggle ,它接收切换的当前值并返回新值。 With this approach you wouldn't need to pass toggle as a dependency to useEffect:使用这种方法,您不需要将toggle作为依赖项传递给 useEffect:

useEffect(() => {
  const handleClick = () => {
    setToggle(currentValue => !currentValue)
  }
  
  window.addEventListener("click", handleClick)

  return () => {
    window.removeEventListener("click", handleClick)
  }
}, [])

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM