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

kar*_*017 15 reactjs

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

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

  useEffect(() => {
    const loc = window.location.pathname
    console.log({ loc })
  }, [window.location.pathname])
Run Code Online (Sandbox Code Playgroud)

Scr*_*jet 25

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

const location = useLocation();
useEffect(() => {
  console.log(location);
}, [location.pathname]);
Run Code Online (Sandbox Code Playgroud)

编辑:这是一个仅用于使用 web 进行反应的解决方案,并且您正在使用react-router-domweb 库。

如果您想在没有安装该库的情况下实现此目的,并且其他答案对您不起作用,您需要执行以下操作:-

  1. 将上下文添加到应用程序的顶层。
export const LocationContext = React.createContext<LocationContextObject>(
  null!
); 
Run Code Online (Sandbox Code Playgroud)
  1. 使用位置设置此上下文提供程序的值

  2. 导航时,您需要使用新位置更新此上下文值,(创建自定义导航功能并在任何地方使用它)

** 灵感来自于remix-run/react-router (react-router-dom)的源码

  • @Sunamin34 - 是的,这可以工作,但OP明确表示**没有任何额外的库** (3认同)

Raf*_*ora 14

创建一个钩子,例如:

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;
};
Run Code Online (Sandbox Code Playgroud)

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

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

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


ani*_*gif 5

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

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
Run Code Online (Sandbox Code Playgroud)

hooks/useIsMounted.ts

import { useState, useEffect } from 'react'

const useIsMounted = (): boolean => {
  const [isMounted, setIsMounted] = useState(false)
  useEffect(() => {
    setIsMounted(() => true)
  }, [])

  return isMounted
}

export default useIsMounted
Run Code Online (Sandbox Code Playgroud)