Je me suis plongé dans un trou de lapin profond avec ce composant pour tenter d'utiliser les hooks React.
Le composant Parent gère un état dictionnaire
qui est finalement distribué à plusieurs composants.
/ p>
Mon composant enfant problématique WordInput
a un formulaire avec une seule entrée. Lors de la soumission du formulaire, le composant récupère la définition du mot à partir d'une API et transmet à la fois le mot et la définition au parent qui définit ensuite l'état sous la forme de dictionnaire
. Jusqu'ici, tout va bien SI c'est le premier mot du dictionnaire
. La partie avec laquelle j'ai des problèmes est de soumettre tous les mots / définitions suivants.
Lorsque l'utilisateur soumet un mot suivant, je veux que le composant vérifie si le mot existe déjà dans le dictionnaire
qui est passé à l'enfant. S'il n'existe pas, ajoutez-le au dictionnaire
via la fonction d'envoi.
Je pense que le problème est que j'essaie d'en faire trop avec useEffect
J' useEffect
pour:
- définir le chargement
- vérifier et traiter le dictionnaire pour les mots existants
- vérifiez que la définition et le mot ne sont pas vides et soumettez les deux au parent / dictionnaire
- chercher une définition à partir d'une API
Dans le code non traité, j'ai plusieurs console.groups
pour m'aider à garder une trace de ce qui se passe. Plus j'ajoute au composant, plus les sous-groupes et sous-groupes de sous-groupes s'accumulent. De toute évidence, l'approche que je prends n'est pas très sèche et provoque trop de ré-rendus des fonctions component / useEffect. Par souci de concision, j'ai retiré les entrées console.log
.
La fetchWordDefinition
importée traite simplement les données extraites et les arrange correctement dans un tableau.
Je ne sais pas comment garder cela sec et efficace, et toute aide est appréciée avec cette tâche plutôt simple. Mon intuition est de garder toute la logique pour soumettre le mot / définition dans le gestionnaire d'envoi, et d'utiliser uniquement useEffect
pour valider les données antérieures.
import React, { useState, useEffect } from "react"; import fetchWordDefinition from "./lib/utils"; const WordInput = ({ onSubmit, dictionary }) => { const [definition, setDefinition] = useState([]); const [cause, setCause] = useState({ function: "" }); const [error, setError] = useState({}); const [loading, setLoading] = useState(false); const [word, setWord] = useState(""); const [wordExistsInDB, setWordExistsInDB] = useState(false); useEffect(() => { const dictionaryEmpty = dictionary.length === 0 ? true : false; if (dictionaryEmpty) { return; } else { for (let i = 0; i < dictionary.length; i += 1) { if (dictionary[i].word === word) { setWordExistsInDB(true); setError({ bool: true, msg: "Word already exists in DB" }); break; } else { setWordExistsInDB(false); setError({ bool: false, msg: "" }); } } } }, [dictionary, word]); useEffect(() => { const definitionNotEmpty = definition.length !== 0 ? true : false; const wordNotEmpty = word !== "" ? true : false; if (wordNotEmpty && definitionNotEmpty && !wordExistsInDB) { onSubmit(word, definition); setWord(""); setDefinition([]); } }, [definition, word, onSubmit, wordExistsInDB]); useEffect(() => { if (cause.function === "fetch") { async function fetchFunction() { const fetch = await fetchWordDefinition(word); return fetch; } fetchFunction().then(definitionArray => { setDefinition(definitionArray); setCause({ function: "" }); }); } }, [cause, word]); const handleSubmit = async e => { e.preventDefault(); setLoading(true); setCause({ function: "fetch" }); }; return ( <form onSubmit={handleSubmit}> {error.bool ? <span>{error.msg}</span> : null} <input name='word' placeholder='Enter Word' type='text' value={word} onChange={({ target: { value } }) => setWord(value)} /> <input type='submit' /> </form> ); }; export default WordInput;
3 Réponses :
Il y a en effet plus de useEffect
que nécessaire, ainsi que la plupart de l'état. Tout ce dont vous avez besoin est le handleSubmit
pour effectuer la récupération.
const WordInput = ({ onSubmit, dictionary }) => { const [word, setWord] = React.useState(""); const handleChange = React.useCallback(e => { setWord(e.currentTarget.value) }, []) const handleSubmit = React.useCallback(() => { //check if word is in dictionary const wordIsAlreadyThere = dictionary.map(entry => entry.word).includes(word) //fetch the definition, wait for it, and call submit if(!wordIsAlreadyThere && word.length > 0){ fetchWordDefinition(word) .then(definition => { onSubmit(word, definition) setWord('') }).catch(err => console.log(err)) } }, []) return ( <form onSubmit={handleSubmit}> <input value={word} onChange={handleChange}/> <input type='submit' /> </form> ); }
Je pense que vous manquez une certaine clarté et à quoi sert useEffect
Un composant fonctionnel est réexécuté à chaque fois qu'un accessoire ou un état change. useEffect
s'exécute lorsque le composant est créé, et nous l'utilisons pour des choses telles que la première extraction ou l'abonnement à un gestionnaire d'événements. Le deuxième argument (tableau de variables) est utilisé pour que, si nous avons par exemple un article de blog avec des commentaires, etc., nous ne récupérons pas tout sauf si l'ID change (ce qui signifie que c'est un nouveau billet de blog)
En regardant votre code, nous avons ce flux:
L'utilisateur entre quelque chose et clique sur Soumettre
Vérifier si le mot existe dans un dictionnaire
a. S'il existe, affichez un message d'erreur
b. S'il n'existe pas, récupérez une API et appelez onSubmit
Donc, vraiment, le seul état que nous ayons ici est le mot. Vous pouvez simplement calculer une erreur en fonction de si le mot est dans le dictionnaire, et l'appel d'API est effectué dans un rappel ( useCallback
). Vous avez beaucoup d’états supplémentaires qui n’ont pas vraiment d’importance dans un état.
Une version simplifiée ressemblerait à ceci
const WordInput = ({ onSubmit, dictionary }) => { const [word, setWord] = useState("") const [loading, setLoading] = useState(false) // `find` will find the first entry in array that matches const wordExists = !!dictionary.find(entry => entry.word === word) // Ternary operator, const error = (wordExists) ? "Word already exists in DB" : null // When user hits submit const handleSubmit = useCallback(() => { if (wordExists || !word.length) return; setLoading(true) fetchFunction() .then(definitionArray => { onSubmit(word, definitionArray) }) }, []) return ( <form onSubmit={handleSubmit}> {error && <span>{error}</span>} <input name='word' placeholder='Enter Word' type='text' value={word} onChange={({ target: { value } }) => setWord(value)} /> <input type='submit' onclick={handleSubmit} disabled={wordExists}/> </form> ); };
Merci pour votre réponse, j'aimerais ajouter ce qui suit. useEffect s'exécute non seulement lorsque le composant est monté, mais également lorsque l'une des variables d'état change. Il semble que setLoading ne soit jamais remis à false dans votre extrait de code.
Oups désolé, je viens de le taper de la mémoire très rapidement. Vous feriez setLoading (false)
dans le .then ()
lorsque l'API a résolu, avant onSubmit ()
. Vous pouvez également définir Word sur une chaîne vide
Votre composant n'a besoin que de garder une trace du mot
et de l'indicateur de chargement
.
useEffect
qui vérifie d'abord si le mot existe déjà. Sinon, il procède à sa récupération et ajoute à la fois le mot et sa définition au dictionnaire. const WordInput = ({ onSubmit, dictionary }) => { const [loading, setLoading] = useState(false); const [word, setWord] = useState(""); useEffect(() => { if (!loading) return; const existing_word = dictionary.find(item => item.word === word); if (existing_word) return; const fetchFunction = async () => { const definition = await fetchWordDefinition(word); // Update the dictionary onSubmit(word, definition); // Reset the component state setWord(""); setLoading(false); }; fetchFunction(); }, [loading]); return ( <form onSubmit={e => { e.preventDefault(); if (word.length) { setLoading(true); } }} > <input name="word" placeholder="Enter Word" type="text" value={word} onChange={({ target: { value } }) => setWord(value)} /> <input type="submit" /> </form> ); };
S'il vous plaît laissez-moi savoir si quelque chose n'est pas clair ou si j'ai manqué quelque chose.