本文介绍了React useState 不会在窗口事件中更新的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

状态确实在滚动上设置,但从事件侦听器记录,它似乎停留在初始值.

State does get set on the scroll, but logged from the eventlistener, it seems to be stuck at the initial value.

我想这与定义副作用时设置的 scrolling 有关系,但是我怎么能从滚动中触发状态更改呢?我假设的任何窗口事件也是如此.

I guess it's something to do with scrolling being set when the side effect's defined, but how could I trigger a state change from a scroll otherwise? Same goes for any window event I presume.

这是一个代码和框示例:https://codesandbox.io/s/react-test-zft3e

Here's a codesandbox example: https://codesandbox.io/s/react-test-zft3e

  const [scrolling, setScrolling] = useState(false);

  useEffect(() => {
    window.addEventListener("scroll", () => {
      console.log(scrolling);
      if (scrolling === false) setScrolling(true);
    });
  }, []);

  return (
    <>
      scrolling: {scrolling}
    </>
  );

推荐答案

所以你的匿名函数被锁定在 scrolling 的初始值上.这就是闭包在 JS 中的工作原理,你最好找一些关于它的漂亮文章,这可能会很棘手,而且钩子严重依赖闭包.

So your anonymous function is locked on initial value of scrolling. It's how closures works in JS and you better find out some pretty article on that, it may be tricky some time and hooks heavily rely on closures.

到目前为止,这里有 3 种不同的解决方案:

So far there are 3 different solutions here:

useEffect(() => {
    const scrollHandler = () => {
      if (scrolling === false) setScrolling(true);
    };
    window.addEventListener("scroll", scrollHandler);
    return () => window.removeEventListener("scroll", scrollHandler);
  }, [scrolling]);

在遵循此路径时,请确保您的 返回清理 函数来自 useEffect.这是很好的默认方法,但滚动它可能影响性能,因为滚动事件触发.

while following this path ensure your are returning cleanup function from useEffect. It's good default approach but for scrolling it may affect performance because scroll event triggers too often.

const scrolling = useRef(false);

  useEffect(() => {
    const handler = () => {
      if (scrolling.current === false) scrolling.current = true;
    };
    window.addEventListener("scroll", handler);
    return () => window.removeEventListener("scroll", handler);
  }, []);

  return (
    <>
      scrolling: {scrolling}
    </>
  );

缺点:更改 ref 不会触发重新渲染.所以你需要有一些其他的变量来改变它触发重新渲染.

downside: changing ref does not trigger re-render. So you need to have some other variable to change it triggering re-render.

(我认为这是这里的首选方式):

(I see it as preferred way here):

useEffect(() => {
    const scrollHandler = () => {
      setScrolling((currentScrolling) => {
        if (!currentScrolling) return true;
        return false;
      });
    };
    window.addEventListener("scroll", scrollHandler);
    return () => window.removeEventListener("scroll", scrollHandler);
}, []);

注意顺便说一句,即使是一次性使用效果,您也最好返回清理功能无论如何.

Note Btw even for one-time use effect you better return cleanup function anyway.

PS 此外,现在您还没有将 scrolling 设置为 false,因此您可以摆脱条件 if(scrolling === false),但可以肯定的是,在现实世界中,您也可能会遇到类似的情况.

PS Also by now you don't set scrolling to false, so you could just get rid of condition if(scrolling === false), but sure in real world scenario you may also run into something alike.

这篇关于React useState 不会在窗口事件中更新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-18 08:55