Verwenden von Immer zum Verwalten des Anwendungsstatus

Der Status wird verwendet, um die Überwachung von Daten aus React-Anwendungen zu organisieren. Der Status ändert sich, wenn Benutzer mit Anwendungen interagieren. Wenn der Benutzer eine Aktion ausführt, müssen wir den Status aktualisieren. Hierbei handelt es sich um einen Datensatz, auf dessen Grundlage das gebildet wird, was der Benutzer auf dem Bildschirm sieht. Aktualisieren Sie den Status von React-Anwendungen mithilfe der setState- Methode.



Da die Zustände nicht direkt aktualisiert werden sollten (in React sollte der Zustand unveränderlich sein), wird die Arbeit mit ihnen mit der Komplikation der Struktur der Zustände zu einer sehr nicht trivialen Aufgabe. Es wird nämlich für den Programmierer schwierig, im Status zu navigieren und seine Daten in der Anwendung zu verwenden.

In solchen Situationen können Sie die Immer-Bibliothek verwenden. Seine Verwendung in React-Anwendungen ist dem Material gewidmet, dessen Übersetzung wir heute veröffentlichen.

Die Grundlagen der Verwendung von Immer in React-Anwendungen


Bei Verwendung von Immer kann die Statusstruktur einer React-Anwendung vereinfacht werden, was die Arbeit erleichtert. Immer verwendet das Konzept des sogenannten "Entwurfs". Ein „Entwurf“ kann als Kopie des Staates angesehen werden, nicht jedoch des Staates selbst.

Immer kopiert den Status sozusagen durch „Drücken“ der CMD + C-Tasten und fügt dann mit den CMD + V-Tasten das, was er kopiert hat, an einer Stelle ein, an der die kopierten Daten angezeigt werden können, ohne das Originalmaterial zu stören. Das Ändern der im Status enthaltenen Daten erfolgt im "Entwurf". Anschließend wird basierend auf den am "Entwurf" vorgenommenen Änderungen der aktuelle Status der Anwendung aktualisiert.

Angenommen, Ihr Anwendungsstatus sieht folgendermaßen aus:

this.state = {   name: 'Kunle',   age: 30,   city: 'Lagos',   country: 'Nigeria' } 

Hier sind die Benutzerdaten. Wie sich herausstellte, feiert dieser Benutzer seinen 31. Geburtstag. Dies bedeutet, dass wir sein Alter ( age ) aktualisieren müssen. Wenn Sie Immer verwenden, um dieses Problem zu lösen, wird zuerst eine Kopie dieses Status erstellt.

Stellen Sie sich nun vor, dass eine Kopie des Vermögens angefertigt wurde, dem Kurier übergeben wurde und er diese Kopie an Kunle übergab. Dies bedeutet, dass es jetzt zwei Kopien des Staates gibt. Eine davon ist der aktuelle Status der Anwendung, und die zweite ist eine „grobe“ Kopie, die an den Benutzer übertragen wurde. Der Benutzer, der den „Entwurf“ bearbeitet, ändert sein Alter auf 31. Danach kehrt der Kurier mit dem geänderten Dokument zurück und gibt den „Entwurf“ an die Anwendung weiter. Dort wird ein Vergleich zweier Versionen des Dokuments durchgeführt, und es werden nur Änderungen bezüglich des Alters des Benutzers am aktuellen Status der Anwendung vorgenommen, da sich am „Entwurf“ nichts anderes geändert hat.

Ein solches Arbeitsschema verstößt nicht gegen die Idee der staatlichen Immunität - der aktuelle Zustand wird nicht direkt aktualisiert. Im Allgemeinen können wir sagen, dass die Verwendung von Immer einfach dazu beiträgt, die Verwendbarkeit des Immunzustands zu verbessern.

Beispiel Nr. 1: Ampel


Schauen wir uns eine Beispielanwendung an, die Immer verwendet. Angenommen, Sie entwickeln eine Ampelanwendung. In dieser Anwendung können Sie versuchen, Immer zu verwenden.

So sieht der Bildschirm dieser Anwendung in einem der Momente ihres Betriebs aus.


Ampelanwendung

Hier finden Sie den Projektcode.

So sieht die Komponente aus, wenn das Projekt Immer verwendet.

 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 ist eine Standardfunktion, die aus Immer importiert wird. Wir übergeben es als Wert an die setState() -Methode. Die produce nimmt eine Funktion an, die als Argument einen draft annimmt. Innerhalb dieser Funktion können wir den Status "Entwurf" bearbeiten und ihn in die Form bringen, die einen realen Status annehmen sollte.

Wenn Ihnen das alles zu kompliziert erscheint, finden Sie hier einen weiteren Ansatz zum Schreiben von Code, der dieselben Aufgaben wie der obige Code löst. Erstellen Sie zunächst eine Funktion:

 const handleLight = (state) => {  return produce(state, (draft) => {    draft.red = 'black';    draft.yellow = 'black';    draft.green = 'green';    draft.next = 'red'  }); } 

An die produce wir als Argumente den aktuellen Status der Anwendung und eine andere Funktion, die den draft übernimmt. Lassen Sie uns nun all dies in der Komponente nutzen:

 handleGreenLight = () => {  const nextState = handleLight(this.state)  this.setState(nextState) } 

Beispiel 2: Einkaufsliste


Wenn Sie schon länger mit React arbeiten, sollten Sie sich nicht über die Spread-Syntax wundern. Wenn Sie Immer verwenden, müssen Sie keine ähnlichen Designs verwenden. Insbesondere beim Arbeiten mit Arrays, die in einem Status enthalten sind.

Wir werden weiterhin die Möglichkeiten von Immer untersuchen und eine Anwendung erstellen, die eine Einkaufsliste implementiert.


Einkaufsliste

Hier können Sie damit experimentieren.

Hier ist die Komponente, mit der wir arbeiten.

 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') ); 

Wenn Sie der Liste neue Einkaufsnotizen hinzufügen, müssen Sie den Status der Komponente aktualisieren, in der im Listenarray neue Elemente gespeichert werden sollen. Um das list mit der Methode setState() zu aktualisieren, benötigen setState() den folgenden Code:

 handleSubmit = (e) => {  e.preventDefault()  const newItem = {    id: uuid.v4(),    name: this.state.name,    price: this.state.price  }  this.setState({ list: [...this.state.list, newItem] }) }; 

Wenn Sie während des Betriebs der Anwendung viele Statuselemente aktualisieren müssen, muss die Spread-Syntax sehr häufig verwendet werden. Ein neuer Zustand wird erhalten, indem das, was sich bereits im Zustand befindet, mit neuen Daten kombiniert wird. Mit zunehmender Anzahl von Änderungen wird die Arbeit komplizierter. Immer verwenden - solche Dinge verursachen keine Schwierigkeiten. Sie können dies überprüfen, indem Sie sich den Beispielcode am Anfang dieses Abschnitts ansehen.

Was aber, wenn wir dem Projekt eine Funktion hinzufügen möchten, die in Form eines Rückrufs nach der Aktualisierung des Status aufgerufen wird? Dies kann beispielsweise erforderlich sein, wenn Sie die Anzahl der Einträge in der Liste oder die Gesamtkosten aller geplanten Einkäufe zählen müssen.

Hier können Sie sich den Code der Anwendung ansehen, den wir nun analysieren werden. Die Schnittstelle ist unten dargestellt.


Anwendung mit der Funktion der Berechnung der Gesamtkosten geplanter Einkäufe

Angenommen, wir möchten den Gesamtwert der geplanten Einkäufe berechnen. Beginnen wir mit der Erstellung eines Statusaktualisierungsmechanismus. Dieser Mechanismus wird durch die handleSubmit Funktion dargestellt:

 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)    }  ) }; 

In der handleSubmit Funktion erstellen wir zunächst ein Objekt basierend auf den vom Benutzer eingegebenen Daten. Der Verweis auf das Objekt wird in die Konstante newItem . Um einen neuen Status der Anwendung zu erstellen, wird die Methode .concat() verwendet. Diese Methode, die für ein Array aufgerufen wird, gibt ein neues Array zurück, das Elemente des ursprünglichen Arrays sowie ein neues Element enthält. Das neue Array wird in draft.list . Danach kann Immer den Status der Anwendung aktualisieren.

Der Rückruf, die calculateAmount Funktion, wird nach einer Statusaktualisierung aufgerufen. Es ist wichtig zu beachten, dass diese Funktion eine aktualisierte Version des Status verwendet.

Die Funktion calculateAmount sieht folgendermaßen aus:

 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    })  ) } 

Haken immer


Use-immer ist ein Hook, mit dem Entwickler den Status von React-Anwendungen steuern können. Schauen wir uns an, wie dieser Hook funktioniert, indem wir die klassische Zähleranwendung auf ihrer Basis implementieren:

 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; 

Die useImmer Funktion useImmer der useState- Methode sehr ähnlich. Die Funktion gibt einen Status und eine Funktion zurück, die den Status aktualisiert. Beim ersten Laden der Komponente entspricht der Inhalt des Status (in diesem Fall die Eigenschaft count ) dem an useImmer Wert. Durch die Verwendung der zurückgegebenen Funktion zum Aktualisieren des Status können wir eine increment erstellen, die den Wert der Eigenschaft count state erhöht.

Und hier ist der Code, der den Hook für Immer verwendet, der an useReducer erinnert:

 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; 

Die Funktion useImmerReducer akzeptiert die useImmerReducer und den Anfangszustand. Es gibt den Status und die dispatch . Danach können Sie den Status für die Anzeige der darin enthaltenen Elemente umgehen. Das Senden von Aktionen mithilfe der dispatch wird ausgeführt, wenn ein neues Element zur Aufgabenliste hinzugefügt wird und wenn die Liste gelöscht wird. Der zu sendenden Aktion wird ein Typ zugewiesen, auf dessen Grundlage in der Reduzierungsfunktion eine Entscheidung darüber getroffen wird, was genau getan werden muss, um eine bestimmte Aktion zu verarbeiten.

Im Reduzierer verwenden wir nach wie vor einen Entitätsentwurf, keinen state . Dank dessen haben wir eine bequeme Möglichkeit, den Status der Anwendung zu verwalten.

Den im vorherigen Beispiel verwendeten Code finden Sie hier .

Zusammenfassung


In diesem Artikel haben wir über Immer gesprochen, eine Bibliothek, die die Verwaltung des Status von React-Anwendungen vereinfacht. Der Autor des Artikels glaubt, dass jeder, der an dieser Bibliothek interessiert ist, Immer in seinen neuen Anwendungen verwenden oder sie langsam in eines der aktuellen Projekte einführen kann.

Hier ist das Material, in dem Sie einige Details zu Immer finden.

Liebe Leser! Planen Sie Immer zu verwenden?

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


All Articles