为什么 useEffect 不会在 window.location.pathname 更改时运行?

IT技术 reactjs
2021-03-25 14:31:52

为什么useEffect不会在window.location.pathname更改时运行loc只登录一次。

在没有任何附加库的情况下,当路径名更改时如何运行useEffect

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

创建一个钩子,例如:

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]);

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

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

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

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

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


相关 - 这是 ESLint 警告,如果您使用,您会看到eslint-plugin-react-hooks

像“window.location.pathname”这样的外部范围值不是有效的依赖项,因为改变它们不会重新渲染组件


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

我不知道为什么,但对我来说,添加'popstate'从未奏效的听众并且我能够useEffect在更改时进行window.location.pathname更改,类似于 karolis 在他们的原始问题中所做的没有问题。这就是我所做的:

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

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