Zustandsverwaltung in React mit Unstated

Avatar of Kingsley Silas
Kingsley Silas am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Wenn Ihre Anwendung komplexer wird, kann die Verwaltung des Zustands mühsam werden. Der Zustand einer Komponente soll in sich geschlossen sein, was das Teilen von Zustand über mehrere Komponenten hinweg zu einem Kopfzerbrechen macht. Redux ist in der Regel die bevorzugte Bibliothek zur Zustandsverwaltung in React. Je nach Komplexität Ihrer Anwendung ist Redux jedoch möglicherweise nicht erforderlich.

Unstated ist eine Alternative, die Ihnen die Funktionalität zur Verwaltung des Zustands über mehrere Komponenten hinweg mit einer Container-Klasse sowie Provider- und Subscribe-Komponenten bietet. Sehen wir uns Unstated in Aktion an, indem wir einen einfachen Zähler erstellen und uns dann eine fortgeschrittenere To-Do-Anwendung ansehen.

Verwendung von Unstated zur Erstellung eines Zählers

Der Code für den Zähler, den wir erstellen, ist auf GitHub verfügbar

Repo ansehen

Sie können Unstated mit Yarn zu Ihrer Anwendung hinzufügen

yarn add unstated

Container

Der Container erweitert die Container-Klasse von Unstated. Er dient ausschließlich der Zustandsverwaltung. Hier wird der Anfangszustand initialisiert und der Aufruf von setState() stattfinden.

import { Container } from 'unstated'

class CounterContainer extends Container {
  state = {
    count: 0
  }

  increment = () => {
    this.setState({ count: this.state.count + 1 })
  }

  decrement = () => {
    this.setState({ count: this.state.count - 1 })
  }
}

export default CounterContainer

Bisher haben wir den Container (CounterContainer) definiert, seinen Startzustand für count auf Null gesetzt und Methoden zum Hinzufügen und Subtrahieren zum Zustand der Komponente in Schritten von Eins definiert.

Sie fragen sich vielleicht, warum wir React zu diesem Zeitpunkt nicht importiert haben. Es ist nicht notwendig, es in den Container zu importieren, da wir überhaupt keinen JSX rendern werden.

Ereignisemitter werden verwendet, um setState() aufzurufen und die Komponenten neu rendern zu lassen. Die Komponenten, die diesen Container nutzen, müssen sich bei ihm anmelden.

Abonnieren

Die Subscribe-Komponente wird verwendet, um den Zustand in die Komponenten einzubinden, die ihn benötigen. Von hier aus können wir die Increment- und Decrement-Methoden aufrufen, die den Zustand der Anwendung aktualisieren und die abonnierte Komponente mit der richtigen Anzahl neu rendern lassen. Diese Methoden werden durch ein paar Schaltflächen ausgelöst, die Ereignishörer enthalten, um die Anzahl zu erhöhen oder zu verringern.

import React from 'react'
import { Subscribe } from 'unstated'

import CounterContainer from './containers/counter'

const Counter = () => {
  return (
    <Subscribe to={[CounterContainer]}>
      {counterContainer => (
        <div>
          <div>
            // The current count value
            Count: { counterContainer.state.count }
          </div>
          // This button will add to the count
          <button onClick={counterContainer.increment}>Increment</button>
          // This button will subtract from the count
          <button onClick={counterContainer.decrement}>Decrement</button>
        </div>
      )}
    </Subscribe>
  )
}

export default Counter

Der Subscribe-Komponente wird der CounterContainer in Form eines Arrays über ihre to-Prop übergeben. Das bedeutet, dass die Subscribe-Komponente mehr als einen Container abonnieren kann und alle Container über die to-Prop der Subscribe-Komponente als Array übergeben werden.

Der counterContainer ist eine Funktion, die eine Instanz jedes Containers erhält, den die Subscribe-Komponente abonniert.

Damit können wir nun auf den Zustand und die Methoden zugreifen, die im Container verfügbar gemacht werden.

Anbieter

Wir werden die Provider-Komponente verwenden, um die Container-Instanzen zu speichern und den Kindern zu ermöglichen, sie zu abonnieren.

import React, { Component } from 'react';
import { Provider } from 'unstated'

import Counter from './Counter'

class App extends Component {
  render() {
    return (
      <Provider>
        <Counter />
      </Provider>
    );
  }
}

export default App;

Damit kann die Counter-Komponente unseren counterContainer nutzen.

Unstated ermöglicht Ihnen die Nutzung aller Funktionalitäten, die setState() von React bietet. Wenn wir beispielsweise den vorherigen Zustand mit einem einzigen Klick dreimal inkrementieren wollen, können wir wie folgt eine Funktion an setState() übergeben

incrementBy3 = () => {
  this.setState((prevState) => ({ count: prevState.count + 1 }))
  this.setState((prevState) => ({ count: prevState.count + 1 }))
  this.setState((prevState) => ({ count: prevState.count + 1 }))
}

Die Idee ist, dass setState() weiterhin so funktioniert wie bisher, aber diesmal mit der Möglichkeit, den Zustand in einer Container-Klasse zu halten. Es wird einfach, den Zustand nur an die Komponenten zu verteilen, die ihn benötigen.

Lassen Sie uns eine To-Do-Anwendung erstellen!

Dies ist eine etwas fortgeschrittenere Anwendung von Unstated. Zwei Komponenten werden den Container abonnieren, der den gesamten Zustand und die Methoden zur Aktualisierung des Zustands verwaltet. Auch hier ist der Code auf Github verfügbar

Repo ansehen

Der Container wird wie folgt aussehen

import { Container } from 'unstated'

class TodoContainer extends Container {
  state = {
    todos: [
      'Mess around with unstated',
      'Start dance class'
    ],
    todo: ''
  };

  handleDeleteTodo = (todo) => {
    this.setState({
      todos: this.state.todos.filter(c => c !== todo)
    })
  }
 
  handleInputChange = (event) => {
    const todo = event.target.value
    this.setState({ todo });
  };

  handleAddTodo = (event) => {
    event.preventDefault()
    this.setState(({todos}) => ({
      todos: todos.concat(this.state.todo)
    }))
    this.setState({ todo: '' });
  }

}

export default TodoContainer

Der Container hat einen anfänglichen todos-Zustand, der ein Array mit zwei Elementen ist. Um To-Do-Elemente hinzuzufügen, haben wir einen todo-Zustand, der auf einen leeren String gesetzt ist.

Wir benötigen eine CreateTodo-Komponente, die den Container abonniert. Jedes Mal, wenn ein Wert eingegeben wird, wird das onChange-Ereignis ausgelöst und dann die Methode handleInputChange() aufgerufen, die wir im Container haben. Das Klicken auf den Submit-Button löst handleAddTodo() aus. Die Methode handleDeleteTodo() empfängt ein To-Do und filtert das To-Do heraus, das mit dem übergebenen übereinstimmt.

import React from 'react'
import { Subscribe } from 'unstated'

import TodoContainer from './containers/todoContainer'

const CreateTodo = () => {
  return (
    <div>
      <Subscribe to={[TodoContainer]}>
        {todos =>
          <div>
            <form onSubmit={todos.handleAddTodo}>
              <input
                type="text"
                value={todos.state.todo}
                onChange={todos.handleInputChange}
              />
              <button>Submit</button>
            </form>
          </div>
        }
      </Subscribe>
    </div>
  );
}

export default CreateTodo

Wenn ein neues To-Do hinzugefügt wird, wird der todos-Zustand, der im Container verfügbar ist, aktualisiert. Die Liste der To-Dos wird vom Container in die Todos-Komponente übernommen, indem die Komponente beim Container angemeldet wird.

import React from 'react';
import { Subscribe } from 'unstated';

import TodoContainer from './containers/todoContainer'

const Todos = () => (
  <ul>
    <Subscribe to={[TodoContainer]}>
      {todos =>
        todos.state.todos.map(todo => (
          <li key={todo}>
            {todo} <button onClick={() => todos.handleDeleteTodo(todo)}>X</button>
          </li>
        ))
      }
    </Subscribe>
  </ul>
);

export default Todos

Diese Komponente durchläuft das Array der im Container verfügbaren To-Dos und rendert sie in einer Liste.

Schließlich müssen wir die Komponenten, die den Container abonnieren, in einen Provider einwickeln, so wie wir es im Fall des Zählers getan haben. Dies tun wir in unserer App.js-Datei genau wie im Zählerbeispiel

import React, { Component } from 'react';
import { Provider } from 'unstated'

import CreateTodo from './CreateTodo'
import Todos from './Todos'

class App extends Component {
  render() {
    return (
      <Provider>
        <CreateTodo />
        <Todos />
      </Provider>
    );
  }
}

export default App;

Zusammenfassend

Es gibt verschiedene Möglichkeiten, den Zustand in React zu verwalten, je nach Komplexität Ihrer Anwendung, und Unstated ist eine praktische Bibliothek, die dies erleichtern kann. Es lohnt sich, den Punkt zu wiederholen, dass Redux, obwohl es großartig ist, nicht immer das beste Werkzeug für den Job ist, auch wenn wir uns in solchen Fällen oft dafür entscheiden. Hoffentlich haben Sie jetzt ein neues Werkzeug in Ihrem Gürtel.