为什么向后滚动时该元素变得固定但不是相对的?

Sus*_*ain 5 javascript frontend reactjs react-hooks

我试图创建一个元素位置:“固定”,在它滚动到屏幕末尾后,每当它返回到第一个屏幕时,它应该再次获得“相对”位置,但状态正在更改为 true (固定)当它滚动到屏幕末尾但当它向后滚动时,它不会更改为 false (相对)。

const [fixed, setFixed] = useState(() => {
    return false;
  });
const scrollHandler = () => {
    if (window.scrollY > window.innerHeight) {

      if (!fixed) {
       
        setFixed(true);
      }
    } else {
      if (fixed) {
        setFixed(false);
      }
    }
  };
  useEffect(() => {
    window.addEventListener("scroll", scrollHandler);

    return () => {
      window.removeEventListener("scroll", scrollHandler);
    };
  }, []);

return (
    <>
      <div
        style={{ position: `${fixed ? "fixed" : "relative"}` }}
        className={classes.navigationBelt}
      ></div>
 </> );
Run Code Online (Sandbox Code Playgroud)

这是代码解释我将状态命名为“fixed”并将初始值设置为“false”

const [fixed, setFixed] = useState(() => {
    return false;
  });

Run Code Online (Sandbox Code Playgroud)

当组件安装到 dom 中时,我在窗口上添加了一个用于滚动的事件监听器。

 useEffect(() => {
    window.addEventListener("scroll", scrollHandler);

    return () => {
      window.removeEventListener("scroll", scrollHandler);
    };
  }, []);
Run Code Online (Sandbox Code Playgroud)

为了处理滚动事件,我创建了一个名为“scrollHandler”的函数

const scrollHandler = () => {
    if (window.scrollY > window.innerHeight) {

      if (!fixed) {
       
        setFixed(true);
      }
    } else {
      if (fixed) {
        setFixed(false);
      }
    }
  };
Run Code Online (Sandbox Code Playgroud)

当滚动超过窗口高度并将元素设置为“固定”时,我将“固定”设置为“真”。注意:我仅在“fixed”状态为 false 时更新状态,否则会导致多次重新渲染。

return (
    <>
      <div
        style={{ position: `${fixed ? "fixed" : "relative"}` }}
        className={classes.navigationBelt}
      ></div>
 </> );
Run Code Online (Sandbox Code Playgroud)

它工作正常,但是当它向后滚动并且“else”条件命中时,状态(“fixed”)仍然显示为 false,即使它已经更改并且元素已经修复。因为 setFixed(false) 不起作用,因为外部条件仍然为 false。

else {
      if (fixed) {
        setFixed(false);
      }
    }
Run Code Online (Sandbox Code Playgroud)

我想知道为什么会发生这种情况?

Gab*_*oli 0

因为您附加到窗口的变量可以在变量处于(初始状态scrollHandler)时访问。fixedfalse

您将其附加到useEffect仅运行一次的文件中,因此它将始终保留scrollHandler.

您可以将fixed作为依赖项传递给useEffect,这样它将刷新/更新事件处理程序。

  useEffect(() => {
    window.addEventListener("scroll", scrollHandler);

    return () => {
      window.removeEventListener("scroll", scrollHandler);
    };
  }, [fixed]);
Run Code Online (Sandbox Code Playgroud)

上面的解决方案有一个“问题”,您需要知道做什么scrollHandler才能指定/更新useEffect.

useEffect更明智的方法是在更改时重新运行,因为它是在(这是真正的依赖项)scrollHandler内部使用的useEffectuseEffect

  useEffect(() => {
    window.addEventListener("scroll", scrollHandler);

    return () => {
      window.removeEventListener("scroll", scrollHandler);
    };
  }, [fixed]);
Run Code Online (Sandbox Code Playgroud)

但是,由于您的代码将scrollHandler在组件的每次重新渲染中更改处理程序,因此您应该尝试使其尽可能“稳定”,即为该函数保留/重新使用相同的引用,而其自己的依赖项是相同的。为此,您可以使用useCallback并将其fixed用作其依赖项。

  useEffect(() => {
    window.addEventListener("scroll", scrollHandler);

    return () => {
      window.removeEventListener("scroll", scrollHandler);
    };
  }, [scrollHandler]);
Run Code Online (Sandbox Code Playgroud)