L'état est utilisé pour organiser la surveillance des données des applications React. Les états changent à mesure que les utilisateurs interagissent avec les applications. Lorsque l'utilisateur effectue une action, nous devons mettre à jour l'état, qui est un ensemble de données sur la base duquel ce que l'utilisateur voit à l'écran est formé. Mettez à jour l'état des applications React à l'aide de la méthode
setState .

Comme les états ne doivent pas être mis à jour directement (dans React, l'état doit être immuable), avec la complication de la structure des états, travailler avec eux se transforme en une tâche très simple. À savoir, il devient difficile pour le programmeur de naviguer dans l'état et d'utiliser ses données dans l'application.
Dans de telles situations, vous pouvez utiliser la bibliothèque Immer. Son utilisation dans les applications React est consacrée au matériel dont nous publions aujourd'hui la traduction.
Les bases de l'utilisation d'Immer dans les applications React
Lorsque vous utilisez Immer, la structure d'état d'une application React peut être simplifiée, ce qui signifie qu'il sera plus facile de travailler avec. Immer utilise le concept de «brouillon». Un «projet» peut être considéré comme une copie de l'État, mais pas l'État lui-même.
Immer, pour ainsi dire, copie l'état en «appuyant» sur les touches CMD + C, puis, à l'aide des touches CMD + V, insère ce qu'il a copié dans un endroit où les données copiées peuvent être visualisées sans perturber le matériel d'origine. La modification des données incluses dans l'état se fait dans le "brouillon", après quoi, sur la base des modifications apportées au "brouillon", l'état actuel de l'application est mis à jour.
Supposons que l'état de votre application ressemble à ceci:
this.state = { name: 'Kunle', age: 30, city: 'Lagos', country: 'Nigeria' }
Voici les données utilisateur. Il s'est avéré que cet utilisateur fête son 31e anniversaire. Cela signifie que nous devons mettre à jour son âge (propriété
age
). Si vous utilisez Immer pour résoudre ce problème, une copie de cet état sera d'abord créée.
Imaginez maintenant qu'une copie de la fortune a été faite, elle a été remise au coursier, et il a remis cette copie à Kunle. Cela signifie qu'il existe maintenant deux copies de l'État. L'un d'eux est l'état actuel de l'application, et le second est une copie «grossière» qui a été transférée à l'utilisateur. L'utilisateur, en éditant le «brouillon», change son âge à 31 ans. Après cela, le coursier revient avec le document modifié et remet le «brouillon» à l'application. Là, une comparaison de deux versions du document est effectuée et seules les modifications concernant l'âge de l'utilisateur sont apportées à l'état actuel de l'application, car rien n'a changé dans le brouillon.
Un tel schéma de travail ne viole pas l'idée d'immunité des États - l'état actuel n'est pas directement mis à jour. En général, nous pouvons dire que l'utilisation d'Immer contribue simplement à améliorer la convivialité de l'état immunitaire.
Exemple n ° 1: feu de circulation
Jetons un coup d'œil à un exemple d'application qui utilise Immer. Supposons que vous développez une application de feux tricolores. Dans cette application, vous pouvez essayer d'utiliser Immer.
Voici à quoi ressemble l'écran de cette application à l'un des moments de son fonctionnement.
Application de feux de circulationIci vous pouvez trouver le code du projet.
Voici à quoi ressemblera le composant, étant donné que le projet utilise Immer.
const {produce} = immer class App extends React.Component { state = { red: 'red', yellow: 'black', green: 'black', next: "yellow" } componentDidMount() { this.interval = setInterval(() => this.changeHandle(), 3000); } componentWillUnmount() { clearInterval(this.interval); } handleRedLight = () => { this.setState( produce(draft => { draft.red = 'red'; draft.yellow = 'black'; draft.green = 'black'; draft.next = 'yellow' }) ) } handleYellowLight = () => { this.setState( produce(draft => { draft.red = 'black'; draft.yellow = 'yellow'; draft.green = 'black'; draft.next = 'green' }) ) } handleGreenLight = () => { this.setState( produce(draft => { draft.red = 'black'; draft.yellow = 'black'; draft.green = 'green'; draft.next = 'red' }) ) } changeHandle = () => { if (this.state.next === 'yellow') { this.handleYellowLight() } else if (this.state.next === 'green') { this.handleGreenLight() } else { this.handleRedLight() } } render() { return ( <div className="box"> <div className="circle" style={{backgroundColor: this.state.red}}></div> <div className="circle" style={{backgroundColor: this.state.yellow}}></div> <div className="circle" style={{backgroundColor: this.state.green}}></div> </div> ); } };
Produce
est une fonctionnalité standard importée d'Immer. Nous la transmettons, en tant que valeur, à la méthode
setState()
. La fonction
produce
prend une fonction qui, en tant qu'argument, prend un
draft
. C'est au sein de cette fonction que nous pouvons éditer l'état «brouillon», le ramenant à la forme qui devrait prendre un état réel.
Si tout cela vous semble trop compliqué - voici une autre approche pour écrire du code qui résout les mêmes tâches que le code ci-dessus. Créez d'abord une fonction:
const handleLight = (state) => { return produce(state, (draft) => { draft.red = 'black'; draft.yellow = 'black'; draft.green = 'green'; draft.next = 'red' }); }
À la fonction
produce
, nous, en tant qu'arguments, transmettons l'état actuel de l'application et une autre fonction qui prend le
draft
argument. Profitons maintenant de tout cela dans le composant:
handleGreenLight = () => { const nextState = handleLight(this.state) this.setState(nextState) }
Exemple 2: liste de courses
Si vous travaillez avec React depuis un certain temps, vous ne devriez pas être surpris de la
syntaxe étendue . Lorsque vous utilisez Immer, vous n'avez pas besoin d'utiliser des conceptions similaires. En particulier, lorsque vous travaillez avec des tableaux contenus dans un état.
Nous continuerons d'explorer les possibilités d'Immer, en créant une application qui implémente une liste de courses.
Liste d'achatsIci, vous pouvez l'expérimenter.
Voici le composant avec lequel nous travaillons.
class App extends React.Component { constructor(props) { super(props) this.state = { item: "", price: 0, list: [ { id: 1, name: "Cereals", price: 12 }, { id: 2, name: "Rice", price: 10 } ] } } handleInputChange = e => { this.setState( produce(draft => { draft[event.target.name] = event.target.value })) } handleSubmit = (e) => { e.preventDefault() const newItem = { id: uuid.v4(), name: this.state.name, price: this.state.price } this.setState( produce(draft => { draft.list = draft.list.concat(newItem) }) ) }; render() { return ( <React.Fragment> <section className="section"> <div className="box"> <form onSubmit={this.handleSubmit}> <h2>Create your shopping list</h2> <div> <input type="text" placeholder="Item's Name" onChange={this.handleInputChange} name="name" className="input" /> </div> <div> <input type="number" placeholder="Item's Price" onChange={this.handleInputChange} name="price" className="input" /> </div> <button className="button is-grey">Submit</button> </form> </div> <div className="box"> { this.state.list.length ? ( this.state.list.map(item => ( <ul> <li key={item.id}> <p>{item.name}</p> <p>${item.price}</p> </li> <hr /> </ul> )) ) : <p>Your list is empty</p> } </div> </section> </React.Fragment> ) } } ReactDOM.render( <App />, document.getElementById('root') );
Lors de l'ajout de nouvelles notes d'achat à la liste, nous devons mettre à jour l'état du composant dans lequel, dans le tableau de
list
, les nouveaux éléments doivent être enregistrés. Pour mettre à jour l'élément de
list
à l'aide de la méthode
setState()
,
setState()
besoin du code suivant:
handleSubmit = (e) => { e.preventDefault() const newItem = { id: uuid.v4(), name: this.state.name, price: this.state.price } this.setState({ list: [...this.state.list, newItem] }) };
Si, pendant le fonctionnement de l'application, vous devez mettre à jour un grand nombre d'éléments d'état, la syntaxe de propagation devra être utilisée très souvent. Un nouvel état est obtenu en combinant ce qui est déjà dans l'état avec de nouvelles données. À mesure que le nombre de changements augmente, le travail devient plus compliqué. Si vous utilisez Immer - de telles choses ne causent pas de difficultés. Vous pouvez le vérifier en consultant l'exemple de code au début de cette section.
Mais que se passe-t-il si nous voulons ajouter une fonction au projet qui, sous la forme d'un rappel, sera appelée après la mise à jour de l'état? Par exemple, cela peut être nécessaire si vous devez compter le nombre d'entrées dans la liste ou le coût total de tous les achats planifiés.
Ici, vous pouvez jeter un œil au code de l'application, que nous allons maintenant analyser. Son interface est illustrée ci-dessous.
Application avec fonction de calcul du coût total des achats planifiésSupposons donc que nous voulons calculer la valeur totale des achats planifiés. Commençons par créer un mécanisme de mise à jour de l'état. Ce mécanisme est représenté par la fonction
handleSubmit
:
handleSubmit = (e) => { e.preventDefault() const newItem = { id: uuid.v4(), name: this.state.name, price: this.state.price } this.setState( produce(draft => { draft.list = draft.list.concat(newItem) }), () => { this.calculateAmount(this.state.list) } ) };
Dans la fonction
handleSubmit
nous créons d'abord un objet basé sur les données saisies par l'utilisateur. La référence à l'objet est écrite dans la constante
newItem
. Pour former un nouvel état de l'application, la méthode
.concat()
est utilisée. Cette méthode, appelée sur un tableau, renvoie un nouveau tableau, qui comprend des éléments du tableau d'origine, ainsi qu'un nouvel élément. Le nouveau tableau est écrit dans
draft.list
. Après cela, Immer peut mettre à jour l'état de l'application.
Le rappel, la fonction
calculateAmount
, est appelé après une mise à jour d'état. Il est important de noter que cette fonction utilise une version mise à jour de l'état.
La fonction
calculateAmount
ressemblera à ceci:
calculateAmount = (list) => { let total = 0; for (let i = 0; i < list.length; i++) { total += parseInt(list[i].price, 10) } this.setState( produce(draft => { draft.totalAmount = total }) ) }
Crochets immers
Use-immer est un hook qui permet à un développeur de contrôler l'état des applications React. Voyons comment fonctionne ce hook en implémentant l'application de compteur classique sur sa base:
import React from "react"; import {useImmer} from "use-immer"; const Counter = () => { const [count, updateCounter] = useImmer({ value: 0 }); function increment() { updateCounter(draft => { draft.value = draft.value +1; }); } return ( <div> <h1> Counter {count.value} </h1> <br /> <button onClick={increment}>Increment</button> </div> ); } export default Counter;
La fonction
useImmer
très similaire à la méthode
useState . La fonction renvoie un état et une fonction qui met à jour l'état. Lorsque le composant est chargé pour la première fois, le contenu de l'état (dans ce cas, la propriété
count
) correspond à la valeur transmise à
useImmer
. L'utilisation de la fonction retournée pour mettre à jour l'état nous permet de créer une fonction d'incrémentation qui incrémente la valeur de la propriété state
count
.
Et voici le code qui utilise le hook pour Immer, qui rappelle
useReducer :
import React, { useRef } from "react"; import {useImmerReducer } from "use-immer"; import uuidv4 from "uuid/v4" const initialState = []; const reducer = (draft, action) => { switch (action.type) { case "ADD_ITEM": draft.push(action.item); return; case "CLEAR_LIST": return initialState; default: return draft; } } const Todo = () => { const inputEl = useRef(null); const [state, dispatch] = useImmerReducer(reducer, initialState); const handleSubmit = (e) => { e.preventDefault() const newItem = { id: uuidv4(), text: inputEl.current.value }; dispatch({ type: "ADD_ITEM", item: newItem }); inputEl.current.value = ""; inputEl.current.focus(); } const handleClear = () => { dispatch({ type: 'CLEAR_LIST' }) } return ( <div className='App'> <header className='App-header'> <ul> {state.map(todo => { return <li key={todo.id}>{todo.text}</li>; })} </ul> <form onSubmit={handleSubmit}> <input type='text' ref={inputEl} /> <button type='submit' > Add Todo </button> </form> <button onClick={handleClear} > Clear Todos </button> </header> </div> ); } export default Todo;
La fonction
useImmerReducer
accepte la fonction de
useImmerReducer
et l'état initial. Il renvoie la fonction d'état et de
dispatch
. Après cela, vous pouvez contourner l'état d'affichage des éléments qu'il contient. L'envoi d'actions à l'aide de la fonction de
dispatch
est effectué lorsqu'un nouvel élément est ajouté à la liste des tâches et lorsque la liste est effacée. L'action à envoyer se voit attribuer un type en fonction duquel une décision est prise dans la fonction de réduction sur ce qui doit être fait exactement pour traiter une action spécifique.
Dans le réducteur, nous utilisons, comme précédemment, une
draft
entité, pas un
state
. Grâce à cela, nous avons un moyen pratique de gérer l'état de l'application.
Le code utilisé dans l'exemple précédent se trouve
ici .
Résumé
Dans cet article, nous avons parlé d'Immer, une bibliothèque qui simplifie la gestion de l'état des applications React. L'auteur de l'article estime que toute personne intéressée par cette bibliothèque peut très bien utiliser Immer dans ses nouvelles applications ou l'introduire lentement dans l'un des projets en cours.
Voici le matériel où vous pouvez trouver quelques détails sur Immer.
Chers lecteurs! Envisagez-vous d'utiliser Immer?
