【问题标题】:useEffect doesn't update state on route changeuseEffect 不会在路由更改时更新状态
【发布时间】:2020-01-25 15:52:08
【问题描述】:

我正在寻找一种解决方案来注册路线更改并使用setStateuseEffect 应用新的state。当路由改变时,下面的当前代码不会更新setState 的函数。

例如,我将/pathname 注册到createContext 内的location.pathname === '/',如果pathname/,则isHomesetState 注册了true,但是如果pathname/page-1 setState 已注册false

在浏览器重新加载时,onMount state 已正确设置,但是在使用 Link 更改路线时,这不会。另外,请注意我正在使用 Gatsby 并在此过程中导入 { Link } from 'gatsby'

CreateContext.js

export const GlobalProvider = ({ children, location }) => {


const prevScrollY = useRef(0);
  const [state, setState] = useState({
    isHome: location.pathname === '/',
    // other states
  });

  const detectHome = () => {
    const homePath = location.pathname === '/';
    if (!homePath) {
      setState(prevState => ({
        ...prevState,
        isHome: false
      }));
    }
    if (homePath) {
      setState(prevState => ({
        ...prevState,
        isHome: true
      }));
    }
  };

  useEffect(() => {
    detectHome();
    return () => {
      detectHome();
    };
  }, [state.isHome]);

  return (
    <GlobalConsumer.Provider
      value={{
        dataContext: state,
      }}
    >
      {children}
    </GlobalConsumer.Provider>
  );
};

如果我在pathname /console.log(state.isHome) 我得到true,我得到false 的任何其他路径名,但是,如果我改变路线,当前的isHome 状态保持以前状态,直到我滚动并useEffect 适用。

注册isHome 状态的目的是改变每页的CSS。

如何在更改路线时使用useEffect 更新状态。以前,我会使用componentDidUpdate 完成此操作,并针对props.location.pathname 注册prevProps.location.pathname,但是,我的理解是useEffect 钩子不再需要这样做。

【问题讨论】:

    标签: javascript reactjs react-hooks gatsby


    【解决方案1】:

    你想要的效果是“当位置改变然后更新我的状态”,这翻译成useEffect代码如下:

      useEffect(() => {
        detectHome();
        return () => {
          detectHome();
        };
      }, [location]);
    

    【讨论】:

    • 但它在页面加载时调用了该函数两次。你能在这里指导一下吗?
    【解决方案2】:

    我认为如果你使用GlobalProvider 作为根组件,在这种情况下,它只会渲染一次,除非有什么东西改变了状态或道具。所以一些解释:

      useEffect(() => {
        detectHome();
        return () => {
          detectHome();
        };
      }, [state.isHome]);
    

    上面这段代码,state只被这个useEffect更新,所以它只会在第一次渲染后更新一次状态,而useEffect的return里面的detectHome只有在其他更新发生时才会执行或state.isHome 与第一次不同。这个解释有点令人困惑,但就是这样。

    但解决方案是使用 window 的 'popstate' 事件:

    export const GlobalProvider = ({ children, location }) => {
    
    
    const prevScrollY = useRef(0);
      const [state, setState] = useState({
        isHome: location.pathname === '/',
        // other states
      });
    
      const detectHome = () => {
        const homePath = location.pathname === '/';
        if (!homePath) {
          setState(prevState => ({
            ...prevState,
            isHome: false
          }));
        }
        if (homePath) {
          setState(prevState => ({
            ...prevState,
            isHome: true
          }));
        }
      };
    
      useEffect(() => {
        window.addEventListener('popstate', detectHome)
        return () => {
          window.removeEventListener('popstate', detectHome)
        };
      }, [state.isHome]);
    
      return (
        <GlobalConsumer.Provider
          value={{
            dataContext: state,
          }}
        >
          {children}
        </GlobalConsumer.Provider>
      );
    };
    

    我认为盖茨比应该操纵历史,所以这会触发浏览器的popstate,你可以检测到url的变化。

    【讨论】:

    • 谢谢 Fuechter,很遗憾,这不起作用。进一步说明,isHome 状态保持为truefalse,并在滚动或其他事件更改时停止更新。
    • This 为我工作。
    【解决方案3】:

    如果您使用 react-router,您可以在 useEffect 中订阅位置更改事件:

    import {browserHistory} from 'react-router';
    
    ...
    useEffect(() => {
      return browserHistory.listen(detectHome);
    }, []);
    ...
    
    

    这将订阅您的 detectHome 函数以在装载时更改位置并在卸载时取消订阅。

    【讨论】:

    • 谢谢 Alex,我相信 Gatsby 使用了 Reach-Router 并且 browserHistory 不可用。我会进一步调查,但感谢您的回答,毫无疑问,它会帮助那些不使用 Gatsby 的人。
    猜你喜欢
    • 2016-10-21
    • 2023-01-26
    • 2020-01-19
    • 2021-07-03
    • 1970-01-01
    • 1970-01-01
    • 2018-01-05
    • 2020-09-05
    • 1970-01-01
    相关资源
    最近更新 更多