【问题标题】:Why useEffect doesn't run on window.location.pathname changes?为什么 useEffect 不在 window.location.pathname 更改上运行?
【发布时间】:2019-10-17 23:57:03
【问题描述】:

为什么 useEffect 不能在 window.location.pathname 更改时运行?我只记录了一次loc

当路径名更改而没有任何其他库时,我如何才能运行 useEffect

  useEffect(() => {
    const loc = window.location.pathname
    console.log({ loc })
  }, [window.location.pathname])

【问题讨论】:

    标签: reactjs


    【解决方案1】:

    创建一个钩子,类似于:

    const useReactPath = () => {
      const [path, setPath] = React.useState(window.location.pathname);
      const listenToPopstate = () => {
        const winPath = window.location.pathname;
        setPath(winPath);
      };
      React.useEffect(() => {
        window.addEventListener("popstate", listenToPopstate);
        return () => {
          window.removeEventListener("popstate", listenToPopstate);
        };
      }, []);
      return path;
    };
    

    然后在你的组件中像这样使用它:

    const path = useReactPath();
    React.useEffect(() => {
      // do something when path changes ...
    }, [path]);
    

    当然,您必须在顶级组件中执行此操作。

    【讨论】:

      【解决方案2】:

      我改编了 Rafael Mora 的答案以适用于整个位置对象,并且还使用 useIsMounted 方法在 Next.js 应用程序的前端工作,并添加了打字稿类型。

      hooks/useWindowLocation.ts

      import useIsMounted from './useIsMounted'
      import { useEffect, useState } from 'react'
      
      
      const useWindowLocation = (): Location|void => {
        const isMounted = useIsMounted()
        const [location, setLocation] = useState<Location|void>(isMounted ? window.location : undefined)
      
        useEffect(() => {
          if (!isMounted) return
      
          const setWindowLocation = () => {
            setLocation(window.location)
          }
      
          if (!location) {
            setWindowLocation()
          }
      
          window.addEventListener('popstate', setWindowLocation)
      
          return () => {
            window.removeEventListener('popstate', setWindowLocation)
          }
        }, [isMounted, location])
      
        return location
      }
      
      export default useWindowLocation
      

      hooks/useIsMounted.ts

      import { useState, useEffect } from 'react'
      
      const useIsMounted = (): boolean => {
        const [isMounted, setIsMounted] = useState(false)
        useEffect(() => {
          setIsMounted(() => true)
        }, [])
      
        return isMounted
      }
      
      export default useIsMounted
      

      【讨论】:

        【解决方案3】:

        useEffect 会在每次渲染组件时进行评估。要订阅对location.pathname 的更改,您需要向window'popstate' 事件添加一个监听器来更新状态,这会告诉组件树重新渲染。

        Rafel Mora's answer 使用setState 实现了一个钩子,这将导致组件重新渲染。然后,您可以使用从 useEffect 中的钩子返回的状态值来代替 window.location.pathname


        相关 - 如果你使用 eslint-plugin-react-hooks,这是 ESLint 警告:

        “window.location.pathname”等外部范围值无效 依赖项,因为改变它们不会重新渲染 组件


        如果你愿意使用库,React Router 提供了一个 useLocation 钩子。

        【讨论】:

          【解决方案4】:

          我不知道为什么,但对我来说,为 'popstate' 添加侦听器从未奏效,当window.location.pathname 发生变化时,我能够让 useEffect 发生变化,类似于 karolis 在他们最初的问题中所做的没有问题。这就是我所做的:

            let path = window.location.pathname;
            /* potentially changes navbar on page change */
            useEffect(() => {
              if (window.location.pathname === "/") {
                setScrollNav(false);
              } else {
                setScrollNav(true);
              }
            }, [path]);
          

          这似乎解决了我遇到的问题,但与其他人相比,我的经历似乎非常不同,所以我很想知道你的想法。

          【讨论】:

            【解决方案5】:

            奇怪的是没有人提到这一点,但是,您可以使用 useLocation 钩子从 react-router-dom 获取位置。所以你可以在依赖数组中使用它。
            Docs here

            const location = useLocation();
            useEffect(() => {
              console.log(location);
            }, [location.pathname]);
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2022-01-20
              • 2023-03-07
              • 2012-07-22
              • 1970-01-01
              相关资源
              最近更新 更多