Utilisation d'Immer pour gérer l'état de l'application React

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 circulation

Ici 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'achats

Ici, 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és

Supposons 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?

Source: https://habr.com/ru/post/fr463337/


All Articles