1
votes

useEffect ne fonctionne pas lorsque l'état change

J'ai un écran (MyPics.js) qui montre une collection d'images que l'utilisateur sélectionne sur un autre écran. J'obtiens les images sélectionnées depuis asyncstorage.

Tout fonctionne bien si je sélectionne des images et que je redémarre le simulateur. Lorsque je navigue vers l'écran MyPics.js, je vois toutes les images stockées dans AsyncStorage à partir des sélections d'images précédentes.

  }, [picList]);

Le terminal affiche:

  I'm in!! picList data is:  //No data, as expected. Nothing is retrieved yet, then...
  Current picList data: pic001,pic007,pic099 // That shows, correctly, what's stored and screen shows those images 

Mais si j'ajoute des images, les nouvelles images n'apparaissent que si je redémarre le simulateur. De plus, pas de lecture dans la console, donc useEffect n'est pas en cours d'exécution pour obtenir les données et réinitialiser l'état.

Je suppose que revenir à l'écran MyPics provoque un nouveau rendu, mais je peux être faux.

J'ai créé 3 boutons sur l'écran MyPics.js qui exécute manuellement soit removeItem ou setItem ou getItem sur asyncstorage qui supprime ou définit ou obtient des images puis met à jour l'état de picList.

Cela fonctionne.

Lorsque j'utilise les boutons, cela récupère ou supprime les images précédemment définies ou définit une nouvelle image et l'écran se rend à nouveau pour montrer le changement d'état.

Donc, useEffect n'est pas en cours d'exécution lorsque je navigue vers l'écran MyPics.js. Il ne fonctionne que sur le premier rendu.

Lorsque j'ajoute l'état picList en tant que dépendance,

  const MyPicsScreen = props => {
    const [picList, setpicList] = useState([]);

    useEffect(() => {
      console.log("I'm in!! picList data is: " +  picList);
      AsyncStorage.getItem("mypics").then((response) => {
      const currentData = JSON.parse(response);
      setpicList(currentData);
      console.log("Current picList data will be: " + currentData);
    });
   }, []);


  return (
<View style={styles.wrapper}>
  <ScrollView>
    <View style={{flexDirection: "column", justifyContent: "center", alignItems: "center"}}>

      {picList ? picList.map((item, index) => {
        return (
            <View key={index} style={styles.viewItem}>
              <Image style={{width: 200, height: 131}} source={Images[PicList[item][3]]} />
            </View>
          );
        }
      ) : <View><Text>You haven't selected any pics yet</Text></View>}

    </View>
  </ScrollView>
</View>
   );
 };

cela fonctionne mais la console fonctionne dans un boucle infinie.

Est-ce normal? Dois-je simplement ignorer la console à défilement infini ou est-ce que je manque une étape? Dois-je utiliser un écouteur de focus pour re-rendre la page lors d'une nouvelle visite car je n'ai besoin des nouvelles données que lorsque je reviens à l'écran MyPics.


0 commentaires

3 Réponses :


1
votes

À partir de la documentation

Si vous souhaitez exécuter un effet et le nettoyer une seule fois (sur le montage et unmount), vous pouvez passer un tableau vide ([]) comme second argument. Cette indique à React que votre effet ne dépend d'aucune valeur des accessoires ou state, il n'a donc jamais besoin d'être réexécuté. Ceci n'est pas traité comme un spécial case - il découle directement de la façon dont le tableau de dépendances fonctionne.

Donc, passer un tableau vide à votre useEffect équivaut à peu près à (sauf useEffect déclenche après la peinture initiale) le componentDidMount Fonctions et componentWillUnmount des composants de classe.


5 commentaires

Exactement. En fait, le problème avec la boucle infinie lorsque vous ajoutez [picList] vient du fait que vous appelez setpicList (currentData) . currentData est un nouvel objet créé (même si le contenu peut être le même, picList n'est pas === currentData ). Ainsi, chaque fois que l'effet est déclenché, il appelle à nouveau le passeur. C'est la boucle infinie


Oui, la boucle ne se produit pas lorsque je place le deuxième argument sur un tableau vide, mais elle ne se met pas à jour lorsque je reviens, sélectionne de nouvelles images, puis retourne à l'écran MyPics. Je dois lui dire de «surveiller» l'état et de le mettre à jour lorsque je revisite la page. Il semble que je doive forcer un nouveau rendu pour qu'il fonctionne correctement.


Vous voudrez peut-être accéder au stockage asynchrone à partir du composant que vous utilisez pour sélectionner de nouvelles images. Ou au moins avertir ce composant des changements d'image afin qu'il sache quand passer l'appel


Cela n'a tout simplement pas de sens de regarder le même état que vous êtes responsable de la mise à jour, vous savez?


Je suppose que c'est exactement à quoi sert l'état, changer et mettre à jour. Ma logique: démarrer l'application. Accédez à l'écran MyPics. Si j'ai déjà choisi des photos, elles s'affichent. Sinon, allez sur un autre écran qui affiche des photos et choisissez-en quelques-unes. Les photos choisies sont ajoutées à asyncstorage. Revenez à la page MyPics. La page est restituée à l'entrée, vérifie le stockage asynchrone avec useEffect, met à jour la page. Problème: useEffect ne revérifie pas le stockage asynchrone lors de la rentrée à l'écran, ce qu'il devrait. La seule façon de le faire est d'ajouter un état comme deuxième argument à useEffect. Mais cela provoque une boucle. C'est certainement une tâche courante.



2
votes

Après beaucoup de peine et de temps perdu, cela fonctionne:

 useEffect(() => {
  AsyncStorage.getItem("mypics").then((response) => {
  setpicList(JSON.parse(response);
});
}, []);

const getList = useCallback(() => {
  AsyncStorage.getItem("mypics").then((value) =>{
  setpicList(JSON.parse(value));
});
}, [picList]);

Quelques exemples très pratiques de composants fonctionnels peuvent être trouvés dans ce fil:

React navigation l'écouteur d'événements didfocus fonctionne différemment entre le composant de classe et le composant fonctionnel a>

Une détection de mise au point est nécessaire lorsque vous revenez à l'écran pour que la fonction useEffect s'exécute à nouveau.


0 commentaires

0
votes

Merci @Kelvin Aitken et @Danny Buonocore pour le soutien

Je me suis demandé pourquoi les hooks useEffect ne fonctionnent que pendant le chargement initial et échouaient chaque fois que j'essayais de paginer plus d'éléments.

J'ai découvert plus tard que je devais passer n'importe quelle dépendance / état dans [] pour dire aux hooks de rendre les changements d'état à tout moment.

const [fetchItemInput, SetFetchItemInput] = useState(input);

const fetchMoreItemBtn = (nextCursor) => {
    const newInput = { ...fetchItemInput };
    newInput.after = nextCursor;
    return SetFetchItemInput(newInput);
  };

  useEffect(() => {

    handleFetchItem(fetchItemInput)
      .then((res) => {
            ...
      })
      .catch((error) => {
            ...
      });
    return () => {};
  }, [fetchItemInput]);       // Any changes run the effect

Merci encore pour votre soutien.


0 commentaires