2
votes

Le composant modal est rendu deux fois à l'ouverture

J'utilise react-spring pour animer un Modal basé sur @ reach / dialog. Le Modal peut avoir n'importe quel enfant. Chez les enfants, je récupère des données basées sur des accessoires.

Le problème est que l'appel de récupération est effectué deux fois à l'ouverture du modal. Je pense que cela a probablement à voir avec la façon dont je gère l'État et qui provoque des rediffusions.

J'ai essayé de mémoriser les enfants dans le modal et cela n'a pas fonctionné, donc je pense que le problème est en dehors du composant Modal.

Voici quelque chose de proche de mon code et de son fonctionnement https://codesandbox.io / s / aimer-liskov-1xouh

EDIT: Je sais déjà que si je supprime l'animation react-spring, le double rendu ne se produit pas, mais je veux essayer de garder l'animation intacte.

Pensez-vous pouvoir m'aider à identifier où se trouve le bogue? (Aussi, quelques conseils sur les bonnes pratiques avec les crochets sont très appréciés).


3 commentaires

il n'est pas appelé deux fois, la requête api donne l'impression d'être appelée deux fois.


@JuniusL. Ce n'est pas qu'il est appelé deux fois, je pense que c'est le rendu qui se produit plus d'une fois. Si je profile le Modal avec les React Devtools, il affiche un total de 5 rendus dans le composant Modal tout au long du cycle d'ouverture et de fermeture.


J'ai essayé d'utiliser React.memo sur l'enfant mais le résultat est le même @filipe. Qu'est-ce qui pourrait être différent avec React.PureComponent?


3 Réponses :


1
votes

J'ai vérifié et il est rendu deux fois à cause de l'animation dans un composant Modal lorsqu'une animation est terminée, modal est rendu une deuxième fois lorsque j'ai commenté le fragment responsable de l'animation, Modal ne rend qu'une seule fois.

 const Modal = ({ children, toggle, isOpen }) => {
  // const transitions = useTransition(isOpen, null, {
  //   from: { opacity: 0 },
  //   enter: { opacity: 1 },
  //   leave: { opacity: 0 }
  // });
  console.log("render");
  const AnimatedDialogOverlay = animated(DialogOverlay);
  // return transitions.map(
  //   ({ item, key, props }) =>
  //     item && (
    return (
        <AnimatedDialogOverlay isOpen={isOpen}>
          <DialogContent>
            <div
              style={{
                display: `flex`,
                width: `100%`,
                alignItems: `center`,
                justifyContent: `space-between`
              }}
            >
              <h2 style={{ margin: `4px 0` }}>Modal Title</h2>
              <button onClick={toggle}>Close</button>
            </div>
            {children}
          </DialogContent>
        </AnimatedDialogOverlay>
    );
  //     )
  // );
};


1 commentaires

Merci pour votre temps et pour votre réponse, malheureusement je le savais déjà, j'espère trouver une solution qui garde l'animation. Je vais modifier la question pour mieux refléter cela



1
votes

il est rendu trois fois car votre composant de retour a transitions.map puisque vous avez trois éléments dans le

  const transitions = useTransition(isOpen, null, {    
    enter: { opacity: 1 }
  });

le {children} code > a été appelé deux fois lorsque isOpen est vrai vous pouvez résoudre le problème en supprimant simplement le de: {opacity: 0} et laissez: {opacity: 0}

alors changez votre modal. js => transitions

    from: { opacity: 0 }
    enter: { opacity: 1 }
    leave: { opacity: 0 }


1 commentaires

Comme vous l'avez dit, le problème était qu'il rendait les enfants à chaque mise à jour. Pour maintenir l'animation, j'ai finalement utilisé la variable d'état que contient la transition et {state === "update" && children} . Maintenant, je n'ai plus qu'à vérifier comment faire le changement de hauteur en entrée / sortie. Merci!!!



1
votes

Le problème, c'est qu'à la fin de l'animation AnotherComponent se remonte. J'ai lu des problèmes similaires sur react-spring. Une façon pourrait être de supprimer l'état d'un autre composant vers l'index.js. De cette façon, l'état ne sera pas perdu lors du remontage et vous pourrez empêcher la récupération des données.

const AnotherComponent = ({ url, todo, setTodo }) => {
  useEffect(() => {
    if (todo.length === 0) {
      axios.get(url).then(res => setTodo(res.data));
    }
  });
....
}

Voici ma version: https://codesandbox.io/s/quiet-pond-idyee


1 commentaires

J'aime vraiment cette solution, mais je ne peux pas extraire la logique des enfants parce qu'il est le responsable de la récupération (dans le code réel, il reçoit un identifiant pour faire la récupération). Mais merci quand même. Cela m'aidera probablement à penser une abstraction.