Verwaltung des Zustands in React mit Unstated-Next

Avatar of Kingsley Silas
Kingsley Silas am

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

In einem früheren Beitrag haben wir gesehen, wie man den Zustand mit Unstated verwaltet. Wie Sie sich vielleicht erinnern, verwendet Unstated Reacts eingebautes setState, um Komponenten zu erstellen, die den Zustand verbrauchen können, indem sie sich an einen Provider abonnieren – wie die Context API von React.

Nun werden wir auf dem letzten Beitrag aufbauen und uns Unstated Next ansehen, eine Bibliothek, die der Autor Jamie Kyle als "spirituellen Nachfolger" seines Unstated-Projekts bezeichnet. Unstated Next bietet sowohl React Hooks als auch die Context API zur Verwaltung des Zustands. Unstated war eine minimale Abstraktion der Idee von React Hooks, bevor diese voll ausgereift waren. Aber jetzt, da Hooks in React so gut sind, ist diese Abstraktion unnötig und Unstated Next integriert sie einfach, während es eine API zum Teilen von Zustand und Logik mit den Hooks bereitstellt.

Wir werden uns speziell ansehen, wie der Zustand in einzelnen und mehreren Komponenten mit Unstated Next verwaltet wird. Es kann hilfreich sein, den vorherigen Beitrag zu Unstated zu lesen, bevor Sie fortfahren, aber es ist nicht unbedingt notwendig.

Beispiel: Eine minimale Formular-Komponente

Zunächst erstellen wir eine winzige React-Anwendung für ein Formular, das lediglich ein Textfeld für den Namen einer Person und einen Button zum Absenden enthält. Wenn der Button geklickt wird, zeigen wir den Namen als Absatz oberhalb des Formulars an. Der Quellcode für dieses Beispiel ist auf GitHub verfügbar.

Dies wird eine Bootstrap React-Anwendung sein, die wir mit Create React App starten können. Installieren wir das und wechseln dann in den Projektordner.

npx create-react-app unstated-next-form
cd unstated-next-form>

Wir müssen Unstated Next als Abhängigkeit hinzufügen

## yarn
yarn add unstated-next

## npm
npm install --save unstated-next

Wir werden React Hooks und createContainer von Unstated Next verwenden, also importieren wir diese in die App-Komponente

// src/App.js
import React, { useState } from 'react';
import { createContainer } from "unstated-next";

Als Nächstes erstellen wir einen benutzerdefinierten Hook. Dort werden wir unseren Zustand speichern, den wir mit useState erstellen können

// src/App.js
// ...same as before

const useForm = () => {
  const [input, setValue] = useState("");
  const [name, setName] = useState("Barney Stinson");

  const handleInput = event => {
    setValue(event.target.value);
  };

  const updateName = event => {
    event.preventDefault();
    setName(input);
    setValue("");
  };

  return {
    input,
    name,
    handleInput,
    updateName,
  };
};

Wir haben hier zwei Zustände definiert. input wird verwendet, um die eingegebenen Werte im Textfeld zu verfolgen und wird mit der Methode handleInput aktualisiert. name wird aktualisiert, wenn der Button geklickt wird, was die Methode updateName auslöst.

OK, jetzt können wir einen Container erstellen, indem wir unseren benutzerdefinierten Hook als Parameter an die Methode createContainer() übergeben.

// src/App.js
// ...same as before

const FormContainer = createContainer(useForm);

Dies erstellt einen Container, den wir in unserer gesamten Anwendung verwenden können. Ja, Sie haben richtig gelesen, aber gehen wir einen Schritt nach dem anderen. Wir beginnen mit dieser einen Komponente, um zu sehen, wie sie mit Unstated Next funktioniert.

Nun erstellen wir eine Form-Komponente, die so aussieht:

// src/App.js
// ...same as before

const Form = () => {
  const form = FormContainer.useContainer();
  return (
    <div>
      <p>Hello! {form.name}</p>
      <div>
        <input
          type="text"
          value={form.input}
          onChange={form.handleInput}
        />
        <button onClick={form.updateName}>Save</button>
      </div>
    </div>
  );
};

Wir weisen die Variable form dem Wert zu, der durch Aufrufen von FormContainer.useContainer() erhalten wird. Der Wert enthält die Zustände und Methoden, die im benutzerdefinierten Hook definiert sind, den wir oben erstellt haben. Damit können wir die bereitgestellten Zustände und Methoden nutzen – aber damit dies geschieht, müssen wir die Form-Komponente in einen Provider einpacken.

const App = () => (
  <Form.Provider>
    <Form />
  </Form.Provider>
)

Versuchen Sie mit dem, was Sie bisher gelernt haben, eine minimale To-Do-Anwendung mit Unstated Next zu erstellen. Wenn Sie nicht weiterkommen, können Sie sich gerne dieses Repository ansehen, um zu sehen, wie ich meine erstellt habe.

Beispiel: Zustand über mehrere Komponenten hinweg teilen

OK, Sie haben vorhin einen Hinweis erhalten, dass wir unseren Formular-Container überall verwenden können, wo wir wollen. Einer der Vorteile der Verwendung von Unstated Next ist, dass es möglich ist, den Zustand über mehrere Komponenten hinweg zu teilen. Um zu sehen, wie das funktioniert, werden wir eine kleine App erstellen, die die oben genannten Formularfunktionen verwendet und auch die Erstellung von To-Do-Aufgaben mit demselben Zustand ermöglicht. Der Name des Benutzers kann in der Formular-Komponente aktualisiert werden, und diese Aktualisierung wird auch in der To-Do-Komponente widergespiegelt. Zwei Fliegen mit einer Klappe!

Es gibt auch ein Repo für dieses Beispiel, also klonen oder laden Sie es gerne herunter, während wir voranschreiten.

Lassen Sie uns ein neues Projekt starten und die notwendigen Abhängigkeiten installieren

npx create-react-app unstated-next-app
cd unstated-next-app
yarn unstated-next shortid

Der Zustand für die Anwendung wird in einer separaten Datei leben. Wir möchten die Zustände für die Formular- und To-Do-Komponenten im Store haben und auch die Methoden, die zu ihrer Aktualisierung benötigt werden. Erstellen Sie eine Datei store.js im Verzeichnis src und gestalten Sie sie wie folgt;

// src/store.js
import { useState } from "react";
import shortid from "shortid"
import { createContainer } from 'unstated-next'
export const useStore = () => {
  // Construct a list that contains two default tasks
  const list = [
    { id: 1, title: 'Write code' },
    { id: 2, title: 'Buy milk' }
  ]
  const [input, setValue] = useState("");
  // Let's set a legen -- wait for it -- dary default name that updates on form submit
  const [name, setName] = useState("Barney Stinson");
  const [todos, addTodo] = useState(list);
  const [item, setTodo] = useState("");
  const handleInput = event => {
    setValue(event.target.value);
  };
  const updateName = event => {
    event.preventDefault();
    setName(input);
    setValue("");
  };
  const handleTodo = event => {
    setTodo(event.target.value);
  };
  const handleSubmit = event => {
    event.preventDefault();
    const value = {
      id: shortid.generate(),
      title: item
    }
    addTodo(todos.concat(value));
    setTodo("");
  };
  return {
    input,
    name,
    handleInput,
    updateName,
    todos,
    item,
    handleTodo,
    handleSubmit
  };
}
export const StoreContainer = createContainer(useStore)

Wir verwenden useState(), um die benötigten Zustände zu erstellen. Die Methoden sind definiert und all dies geschieht innerhalb des benutzerdefinierten Hooks useStore(). Wir erstellen den StoreContainer und übergeben dann useStore() als Parameter an createContainer(). Damit können wir den StoreContainer in den notwendigen Komponenten verwenden, wo wir die von uns definierten Zustände und Methoden nutzen möchten.

Beginnen wir mit dem Formularteil. Erstellen Sie eine Datei namens form.js und sie sollte so aussehen, wie ich sie unten habe;

// src/form.js
import React from "react";
import { StoreContainer} from "./store";

const FormComponent = () => {
  const form = StoreContainer.useContainer();
  return (
    <div>
      <p>Hello! {form.name}</p>
      <div>
        <input type="text" value={form.input} onChange={form.handleInput} />
        <button onClick={form.updateName}>Change Name</button>
      </div>
    </div>
  );
};
export default FormComponent;

Wir verwenden StoreContainer, um auf die benötigten Zustände und Methoden zuzugreifen. Wir werden dasselbe für die Aufgaben-Komponente tun, die Sie in einer todo.js-Datei erstellen können.

// src/todo.js
import React from "react";
import { StoreContainer } from "./store";

const TodoComponent = () => {
  const todo = StoreContainer.useContainer();
  return (
    <div>
      <p>Add Todos</p>
      <input type="text" value={todo.item} onChange={todo.handleTodo} />
      <button onClick={todo.handleSubmit}>Add</button>
      <div>
        <p>Dear {todo.name}, here are your current tasks;</p>
        {todo.todos.map((item) => {
          return (
            <ul key={item.id}>
              <li>{item.title}</li>
            </ul>
          );
        })}
      </div>
    </div>
  );
};
export default TodoComponent;

Sie sehen, dass todo.name nur in der FormComponent aktualisiert werden kann. Das liegt daran, dass wir eine Möglichkeit benötigen, den Zustand in beiden Komponenten bereitzustellen. Deshalb werden wir uns wieder an Provider wenden und einen in der App-Komponente hinzufügen, so wie wir es im vorherigen Beispiel getan haben.

import React from 'react';
import TodoComponent from "./todo";
import FormComponent from "./form";
import { StoreContainer } from "./store"

function App() {
  return (
    <div className="App">
      <StoreContainer.Provider>
        <FormContainer />
        <TodoContainer />
      </StoreContainer.Provider>
    </div>
  );
}
export default App;

Da haben wir es! Durch das Hinzufügen des Providers können Daten aus der Formular-Komponente genommen, im Provider gespeichert und wieder an die Aufgabenliste übergeben werden. 💥