Comment créer un crochet React personnalisé pour récupérer et mettre en cache des données
componentDidMount ()
mais avec l'introduction des crochets, vous pouvez créer un crochet personnalisé qui récupérera et mettra en cache les données pour vous. C'est ce que ce didacticiel couvrira.
Si vous êtes un débutant dans React Hooks, vous pouvez commencer par vérifier la documentation officielle de pour en avoir une idée. Après cela, je recommanderais de lire « Mise en route de l'API React Hooks » de Shedrack Akintayo. Pour vous assurer que vous suivez, il y a aussi un article écrit par Adeneye David Abiodun qui couvre les meilleures pratiques avec React Hooks qui, je suis sûr, vous sera utile.
Tout au long de cet article , nous utiliserons Hacker News Search API pour créer un hook personnalisé que nous pouvons utiliser pour récupérer des données. Bien que ce didacticiel couvre l'API Hacker News Search, le crochet fonctionnera de manière à ce qu'il renvoie la réponse de tout lien API valide que nous lui transmettons.
Meilleures pratiques avec React [19659007] React est une fantastique bibliothèque JavaScript pour créer des interfaces utilisateur riches. Il fournit une excellente abstraction de composants pour organiser vos interfaces en un code qui fonctionne bien, et il y a à peu près tout ce que vous pouvez l'utiliser. Lire plus d'articles sur React →
Récupération de données dans un composant React
Avant les hooks React, il était classique de récupérer les données initiales dans le composant composantDidMount ()
méthode de cycle de vie et des données basées sur prop ou changements d'état dans componentDidUpdate ()
méthode de cycle de vie.
Voici comment cela fonctionne:
componentDidMount () {
const fetchData = async () => {
réponse const = attendre la récupération (
`https: //hn.algolia.com/api/v1/search? query = JavaScript`
);
const data = attente de response.json ();
this.setState ({data});
};
fetchData ();
}
componentDidUpdate (previousProps, previousState) {
if (previousState.query! == this.state.query) {
const fetchData = async () => {
réponse const = attendre la récupération (
`https://hn.algolia.com/api/v1/search?query=$ {this.state.query}`
);
const data = attente de response.json ();
this.setState ({data});
};
fetchData ();
}
}
La méthode de cycle de vie componentDidMount
est invoquée dès que le composant est monté, et lorsque cela est fait, ce que nous avons fait a été de faire une demande de recherche de «JavaScript» via l'API Hacker News et de mettre à jour l'état basé sur la réponse.
La méthode de cycle de vie componentDidUpdate
en revanche, est invoquée en cas de modification du composant. Nous avons comparé la requête précédente dans l'état avec la requête actuelle pour éviter que la méthode ne soit invoquée chaque fois que nous définissons «data» dans l'état. Une chose que nous obtenons de l'utilisation des crochets est de combiner les deux méthodes de cycle de vie de manière plus propre, ce qui signifie que nous n'aurons pas besoin de deux méthodes de cycle de vie pour le montage et la mise à jour du composant.
Récupération de données avec useEffect
Hook
Le useEffect
est appelé dès que le composant est monté. Si nous avons besoin que le hook soit réexécuté en fonction de certains changements de prop ou d'état, nous devons les transmettre au tableau de dépendances (qui est le deuxième argument du hook useEffect
).
Explorons comment pour récupérer des données avec des hooks:
import {useState, useEffect} de 'react';
const [status, setStatus] = useState ('inactif');
const [query, setQuery] = useState ('');
const [data, setData] = useState ([]);
useEffect (() => {
if (! query) return;
const fetchData = async () => {
setStatus ('récupération');
réponse const = attendre la récupération (
`https://hn.algolia.com/api/v1/search?query=$ {query}`
);
const data = attente de response.json ();
setData (data.hits);
setStatus ('récupéré');
};
fetchData ();
}, [query]);
Dans l'exemple ci-dessus, nous avons passé la requête
en tant que dépendance à notre crochet useEffect
. Ce faisant, nous indiquons useEffect
pour suivre les modifications de requête. Si la valeur de la requête précédente
n'est pas la même que la valeur actuelle, la useEffect
est de nouveau invoquée.
Cela dit, nous définissons également plusieurs statuts
sur le composant selon les besoins, car cela transmettra mieux un message à l'écran en fonction du statut de certains états finis
. Dans l'état inactif nous pouvions faire savoir aux utilisateurs qu'ils pouvaient utiliser la zone de recherche pour commencer. Dans l'état allant chercher nous pourrions montrer une fileuse . Et, dans l'état récupéré nous rendrons les données.
Il est important de définir les données avant d'essayer de définir le statut sur récupéré
afin d'éviter un scintillement. qui se produit lorsque les données sont vides pendant que vous définissez le statut récupéré
.
Création d'un crochet personnalisé
«Un crochet personnalisé est une fonction JavaScript dont le nom commence par« utiliser » et cela peut appeler d'autres Hooks. "
C'est vraiment ce que c'est, et avec une fonction JavaScript, il vous permet de réutiliser un morceau de code dans plusieurs parties de votre application. [19659005] La définition des React Docs l'a révélée mais voyons comment cela fonctionne dans la pratique avec un compteur de hook personnalisé:
const useCounter = (initialState = 0) => {
const [count, setCount] = useState (initialState);
const add = () => setCount (count + 1);
const soustract = () => setCount (count - 1);
return {count, add, soustract};
};
Ici, nous avons une fonction régulière où nous prenons un argument facultatif, définissons la valeur à notre état, ainsi que les méthodes add
et soustract
qui pourraient être
Partout dans notre application où nous avons besoin d'un compteur, nous pouvons appeler useCounter
comme une fonction régulière et passer un initialState
afin que nous sachions où commencer à compter à partir de . Lorsque nous n'avons pas d'état initial, nous passons par défaut à 0.
Voici comment cela fonctionne dans la pratique:
import {useCounter} de './customHookPath';
const {count, add, subtract} = useCounter (100);
eventHandler (() => {
ajouter(); // ou soustraire ();
});
Ce que nous avons fait ici a été d'importer notre hook personnalisé à partir du fichier dans lequel nous l'avons déclaré, afin de pouvoir l'utiliser dans notre application. Nous définissons son état initial à 100, donc chaque fois que nous appelons add ()
il augmente le nombre
de 1, et chaque fois que nous appelons soustrait ()
il diminue compter
par 1.
Créer useFetch
Hook
Maintenant que nous avons appris à créer un hook personnalisé simple, extrayons notre logique pour extraire des données dans un hook personnalisé .
const useFetch = (query) => {
const [status, setStatus] = useState ('inactif');
const [data, setData] = useState ([]);
useEffect (() => {
if (! query) return;
const fetchData = async () => {
setStatus ('récupération');
réponse const = attendre la récupération (
`https://hn.algolia.com/api/v1/search?query=$ {query}`
);
const data = attente de response.json ();
setData (data.hits);
setStatus ('récupéré');
};
fetchData ();
}, [query]);
return {status, data};
};
C'est à peu près la même chose que nous avons fait ci-dessus à l'exception qu'il s'agit d'une fonction qui prend en la requête
et renvoie le statut
et les données
. Et, c'est un crochet useFetch
que nous pourrions utiliser dans plusieurs composants de notre application React.
Cela fonctionne, mais le problème avec cette implémentation est maintenant, il est spécifique à Hacker News donc nous pourrions simplement l'appeler useHackerNews
. Ce que nous avons l'intention de faire est de créer un hook useFetch
qui peut être utilisé pour appeler n'importe quelle URL. Réorganisons-le pour prendre une URL à la place!
const useFetch = (url) => {
const [status, setStatus] = useState ('inactif');
const [data, setData] = useState ([]);
useEffect (() => {
if (! url) return;
const fetchData = async () => {
setStatus ('récupération');
réponse const = attente de récupération (url);
const data = attente de response.json ();
setData (données);
setStatus ('récupéré');
};
fetchData ();
}, [url]);
return {status, data};
};
Maintenant, notre hook useFetch est générique et nous pouvons l'utiliser comme nous le voulons dans nos différents composants.
Voici une façon de le consommer:
const [query, setQuery] = useState ('');
const url = query && `https://hn.algolia.com/api/v1/search?query=$ {query}`;
const {status, data} = useFetch (url);
Dans ce cas, si la valeur de requête
est véridique
nous allons de l'avant pour définir l'URL et si ce n'est pas le cas, nous pouvons passer indéfini comme il le ferait. être manipulé dans notre crochet. L'effet tentera de s'exécuter une fois, malgré tout.
Mémorisation des données récupérées
La mémorisation est une technique que nous utiliserions pour nous assurer que nous n'atteignons pas le point d'extrémité hackernews
si nous avons créé une sorte de demande pour le récupérer à une phase initiale. Le stockage du résultat d'appels de récupération coûteux permettra aux utilisateurs d'économiser du temps de chargement, augmentant ainsi les performances globales.
Remarque : Pour plus de contexte, vous pouvez consulter l'explication de Wikipedia sur la mémorisation. .
Explorons comment nous pourrions faire cela!
const cache = {};
const useFetch = (url) => {
const [status, setStatus] = useState ('inactif');
const [data, setData] = useState ([]);
useEffect (() => {
if (! url) return;
const fetchData = async () => {
setStatus ('récupération');
if (cache [url]) {
const data = cache [url];
setData (données);
setStatus ('récupéré');
} autre {
réponse const = attente de récupération (url);
const data = attente de response.json ();
cache [url] = données; // définir la réponse dans le cache;
setData (données);
setStatus ('récupéré');
}
};
fetchData ();
}, [url]);
return {status, data};
};
Ici, nous mappons les URL à leurs données. Donc, si nous faisons une demande pour récupérer des données existantes, nous définissons les données à partir de notre cache local, sinon, nous allons faire la demande et définir le résultat dans le cache. Cela garantit que nous ne faisons pas d'appel API lorsque nous avons les données à notre disposition localement. Nous remarquerons également que nous supprimons l'effet si l'URL est falsifiée
de sorte qu'il s'assure que nous ne procédons pas à l'extraction de données qui n'existent pas. Nous ne pouvons pas le faire avant le hook useEffect
car cela irait à l'encontre d'une des règles des hooks, qui est d'appeler toujours les hooks au niveau supérieur.
Déclarer cache
dans une portée différente fonctionne mais cela rend notre crochet aller à l'encontre du principe d'une fonction pure . En outre, nous voulons également nous assurer que React aide à nettoyer notre gâchis lorsque nous ne voulons plus utiliser le composant. Nous allons explorer useRef
pour nous aider à y parvenir.
Mémorisation des données avec useRef
"
useRef
est comme une boîte qui peut contenir un mutable value in its.current property
. ”
Avec useRef
nous pouvons définir et récupérer facilement des valeurs mutables et sa valeur persiste tout au long de
Remplaçons notre implémentation de cache par un peu de magie useRef
!
const useFetch = (url) => {
const cache = useRef ({});
const [status, setStatus] = useState ('inactif');
const [data, setData] = useState ([]);
useEffect (() => {
if (! url) return;
const fetchData = async () => {
setStatus ('récupération');
if (cache.current [url]) {
const data = cache.current [url];
setData (données);
setStatus ('récupéré');
} autre {
réponse const = attente de récupération (url);
const data = attente de response.json ();
cache.current [url] = données; // définir la réponse dans le cache;
setData (données);
setStatus ('récupéré');
}
};
fetchData ();
}, [url]);
return {status, data};
};
Ici, notre cache est maintenant dans notre crochet useFetch
avec un objet vide comme valeur initiale.
Wrapping Up
Eh bien, j'ai déclaré que définir les données avant de définir le statut récupéré était une bonne idée, mais il y a aussi deux problèmes potentiels que nous pourrions avoir avec cela:
- Notre test unitaire pourrait échouer car le tableau de données n'est pas vide pendant que nous sommes dans l'état de récupération. React pourrait en fait changer d'état par lots, mais il ne peut pas le faire s'il est déclenché de manière asynchrone;
- Notre application restitue plus qu'elle ne devrait.
Faisons un nettoyage final de notre useFetch
hook., Nous allons commencer par changer nos useState
en un useReducer
. Voyons comment cela fonctionne!
const initialState = {
État inactif',
erreur: null,
données: [],
};
const [state, dispatch] = useReducer ((état, action) => {
commutateur (action.type) {
cas 'FETCHING':
return {... initialState, status: 'fetching'};
cas 'FETCHED':
return {... initialState, status: 'fetched', data: action.payload};
cas 'FETCH_ERROR':
return {... initialState, status: 'error', error: action.payload};
défaut:
état de retour;
}
}, Etat initial);
Ici, nous avons ajouté un état initial qui est la valeur initiale que nous avons transmise à chacun de nos useState
s. Dans notre useReducer
nous vérifions le type d'action que nous souhaitons effectuer et définissons les valeurs appropriées à indiquer en fonction de cela.
Cela résout les deux problèmes dont nous avons discuté précédemment, car nous arrivons maintenant à définir l'état et les données en même temps afin d'éviter des états impossibles et des rendus inutiles.
Il ne reste plus qu'une chose: nettoyer nos effets secondaires. Fetch implémente l'API Promise, en ce sens qu'elle pourrait être résolue ou rejetée. Si notre hook tente de faire une mise à jour alors que le composant a été démonté à cause d'une promesse
qui vient d'être résolue, React retournerait Impossible d'effectuer une mise à jour de l'état React sur un composant non monté.
[19659005] Voyons comment résoudre ce problème avec le nettoyage useEffect
!
useEffect (() => {
laissez cancelRequest = false;
if (! url) return;
const fetchData = async () => {
expédition ({type: 'FETCHING'});
if (cache.current [url]) {
const data = cache.current [url];
dispatch ({type: 'FETCHED', charge utile: données});
} autre {
essayez {
réponse const = attente de récupération (url);
const data = attente de response.json ();
cache.current [url] = données;
if (cancelRequest) return;
dispatch ({type: 'FETCHED', charge utile: données});
} catch (erreur) {
if (cancelRequest) return;
dispatch ({type: 'FETCH_ERROR', charge utile: error.message});
}
}
};
fetchData ();
return function cleanup () {
cancelRequest = true;
};
}, [url]);
Ici, nous avons défini cancelRequest
sur true
après l'avoir défini à l'intérieur de l'effet. Donc, avant d'essayer de faire des changements d'état, nous confirmons d'abord si le composant a été démonté. S'il a été démonté, nous ignorons la mise à jour de l'état et s'il n'a pas été démonté, nous mettons à jour l'état. Cela résoudra l'erreur React state update et empêchera également les conditions de concurrence dans nos composants.
Conclusion
Nous avons exploré plusieurs concepts de hooks pour aider à extraire et mettre en cache les données dans nos composants. Nous avons également nettoyé notre crochet useEffect
qui permet d'éviter un bon nombre de problèmes dans notre application.
Si vous avez des questions, n'hésitez pas à les déposer dans la section commentaires ci-dessous! [19659069] Références

Source link