web-dev-qa-db-fra.com

useEffect ne met pas à jour l'état lors du changement d'itinéraire

Je recherche une solution pour enregistrer le changement d'itinéraire et appliquer de nouveaux state en utilisant setState et useEffect. Le code actuel ci-dessous ne met pas à jour les fonctions de setState lorsque l'itinéraire est modifié.

Par exemple, j'enregistre le pathname de / Avec location.pathname === '/' Dans createContext, si le pathname est / Le setState de isHome est enregistré true, cependant si pathname est /page-1setState est enregistré false.

Lors des rechargements du navigateur, onMount le state est correctement défini, mais ce n'est pas le cas lors d'un changement d'itinéraire utilisant Link. Veuillez également noter que j'utilise Gatsby et, ce faisant, j'importe { 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>
  );
};

Si je console.log(state.isHome) sur pathname/ J'obtiens true, tout autre chemin que j'obtiens false, cependant, si je change de route, l'état actuel de isHome reste précédent, jusqu'à ce que je défile et que useEffect s'applique.

L'enregistrement de l'état isHome a pour but de modifier le CSS par page.

Comment puis-je mettre à jour l'état avec useEffect lors du changement de route. Auparavant, j'aurais fait cela avec componentDidUpdate et enregistré prevProps.location.pathname Contre props.location.pathname, Cependant, je crois comprendre que ce n'est plus nécessaire avec le crochet useEffect .

3
Darren

Si vous utilisez react-router, vous pouvez vous abonner à l'événement de changement d'emplacement dans votre utilisation

import {browserHistory} from 'react-router';

...
useEffect(() => {
  return browserHistory.listen(detectHome);
}, []);
...

Cela souscrirait votre fonction detectHome pour le changement d'emplacement lors du montage et la désinscrire lors du démontage.

0
Alex Gessen

Je pense que si vous utilisez GlobalProvider comme composant racine, dans ce cas, il ne sera rendu qu'une seule fois, sauf si quelque chose change les états ou les accessoires. Donc, une explication:

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

Ce code ci-dessus, le state est mis à jour uniquement par ce useEffect, donc il ne met à jour l'état qu'une seule fois après le premier rendu, et le detectHome à l'intérieur du retour de useEffect ne s'exécute que lorsqu'une autre mise à jour se produit ou que state.isHome est différent de la première fois. C'est un peu confus cette explication, mais c'est.

Mais pour la solution, utilisez l'événement "popstate" de la fenêtre:

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

Je pense que Gatsby devrait manipuler l'historique, donc cela déclenchera popstate du navigateur, et vous pourrez détecter les changements de l'url.

0
Fuechter