別の日、Reactでアプリケーションの状態を管理する別の方法! Unstated は、 @jamiebuilds による新しいライブラリであり、Reactの新しいコンテキストAPI を使用して、状態を非常に簡単に管理できるようにします。

Unstatedの使い方を見てみましょう。心配しないでください。まったく痛みはありません!

インストール

npmまたはYarnを使用してunstatedパッケージをインストールするだけです。

$ npm install unstated

# or
$ yarn add unstated

使用法

Unstatedは、 container クラス、 Subscription コンポーネント、およびProviderコンポーネントの3つを提供します。

容器

UnstatedのContainerクラスを拡張して、状態の一部のコンテナーを作成します。

./containers/todo.js
import { Container } from 'unstated';

class TodoContainer extends Container {
  // ...
}

export default TodoContainer;

また、コンテナでは、statesetStateを使用して状態を管理します。これは、Reactでローカルコンポーネントの状態を管理するのにすでに慣れているためです。

./containers/todo.js
import { Container } from 'unstated';

class TodoContainer extends Container {
  state = {
    todos: []
  };

  id = 0;

  addTodo = todo => {
    const newTodo = { id: this.id++, marked: false, description: todo };
    this.setState({
      todos: [...this.state.todos, newTodo]
    });
  };

  removeTodo = id => {
    this.setState({
      todos: this.state.todos.filter(todo => todo.id !== id)
    });
  };

  markTodo = id => {
    this.setState({
      todos: this.state.todos.map(todo => {
        if (todo.id !== id) {
          return todo;
        } else {
          return { ...todo, marked: !todo.marked };
        }
      })
    });
  };
}

export default TodoContainer;

ご覧のとおり、ToDoのすべてのビジネスロジックはコンテナ内に含まれています。 Reactコンポーネントは、このコンテナーから状態をサブスクライブし、コンテナーで定義されたメソッドを呼び出すことで状態を変更できます。 次に、サブスクライバーコンポーネントの再レンダリングをトリガーして、コンテナー内の状態を変更しました。

プロバイダー

Provider コンポーネントは、コンテナーインスタンスを格納するために使用され、その子がインスタンスにサブスクライブできるようにします。 コンテナをサブスクライブするコンポーネントのトップレベルで使用するだけです。

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

import Todos from './Todos';
import AddTodo from './AddTodo';

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

export default App;

申し込む

そして最後に、Unstatedの Subscribe コンポーネントは、コンテナーの配列を含む to プロップを受け取り、その children プロップとしての関数を期待します( renderprop[を参照] X184X])サブスクライブする各コンテナのインスタンスを受け取ります。

この例では、AddTodoコンポーネントは次のようになります。

AddTodo.js
import React from 'react';
import { Subscribe } from 'unstated';

import TodoContainer from './containers/todo';

class AddTodo extends React.Component {
  inputRef = React.createRef();

  handleClick = addTodo => {
    if (this.inputRef.current.value) {
      addTodo(this.inputRef.current.value);
      this.inputRef.current.value = '';
    }
  };

  render() {
    return (
      <div>
        <input type="text" placeholder="your new todo" ref={this.inputRef} />

        <Subscribe to={[TodoContainer]}>
          {todoContainer => (
            <button onClick={() => this.handleClick(todoContainer.addTodo)}>
              Add
            </button>
          )}
        </Subscribe>
      </div>
    );
  }
}

export default AddTodo;

ToDoコンテナインスタンスにアクセスし、そのインスタンスを呼び出す方法に注目してください addTodo 方法。 ここでは、Reactの新しいcreateRefAPIも利用しています。


また、TodosコンポーネントでUnstatedのSubscribeコンポーネントを使用して、Todoコンテナをサブスクライブし、その状態に含まれるTodoを表示して、完了としてマークするか、削除できるようにします。

Todos.js
import React from 'react';
import { Subscribe } from 'unstated';

import TodoContainer from './containers/todo';

class Todos extends React.Component {
  render() {
    return (
      <ul>
        <Subscribe to={[TodoContainer]}>
          {todoContainer =>
            todoContainer.state.todos.map(todo => (
              <li key={todo.id}>
                <span
                  className={todo.marked ? 'marked' : null}
                  onClick={() => todoContainer.markTodo(todo.id)}
                >
                  {todo.description}
                </span>
                <button onClick={() => todoContainer.removeTodo(todo.id)}>
                  X
                </button>
              </li>
            ))
          }
        </Subscribe>
      </ul>
    );
  }
}

export default Todos;

複数のコンテナ

さまざまな状態を独自の個別のコンテナに分離するのも同じくらい簡単です。 たとえば、アプリでタスクも処理する必要があるとします。 AddTodoコンポーネントをTaskContainerにもサブスクライブするPASSWORDコンポーネントにリファクタリングしてみましょう。

いくつかのラジオボタンを使用して、ユーザーがToDoを追加するかタスクを追加するかを選択できるようにします。また、参照を使用する代わりに、テキスト入力要素を制御された入力に変更します。

PASSWORD.js
import React from 'react';
import { Subscribe } from 'unstated';

import TodoContainer from './containers/todo';
import TaskContainer from './containers/task';

class AddItem extends React.Component {
  state = {
    itemValue: '',
    itemChoice: 'todo'
  };

  handleInputChange = e => {
    this.setState({
      itemValue: e.target.value
    });
  };

  handleRadioChange = e => {
    this.setState({
      itemChoice: e.target.value
    });
  };

  handleClick = (addTodo, addTask) => {
    if (this.state.itemValue && this.state.itemChoice === 'todo') {
      addTodo(this.state.itemValue);
    } else if (this.state.itemValue) {
      addTask(this.state.itemValue);
    }

    this.setState({
      itemValue: ''
    });
  };

  render() {
    return (
      <div>
        <input
          type="text"
          placeholder="your new item"
          value={this.state.itemValue}
          onChange={this.handleInputChange}
        />

        <input
          type="radio"
          id="todoItem"
          name="itemType"
          value="todo"
          checked={this.state.itemChoice === 'todo'}
          onChange={this.handleRadioChange}
        />
        <label htmlFor="todoItem">Todo</label>

        <input
          type="radio"
          id="taskItem"
          name="itemType"
          value="task"
          checked={this.state.itemChoice === 'task'}
          onChange={this.handleRadioChange}
        />
        <label htmlFor="taskItem">Task</label>

        <Subscribe to={[TodoContainer, TaskContainer]}>
          {(todoContainer, taskContainer) => (
            <button
              onClick={() =>
                this.handleClick(todoContainer.addTodo, taskContainer.addTask)
              }
            >
              Add {this.state.itemChoice}
            </button>
          )}
        </Subscribe>
      </div>
    );
  }
}

export default AddItem;

これで、コンポーネントツリー内のタスクとタスクの表示を分離できます。TodosコンポーネントはTodoContainerにサブスクライブし、Tasksコンポーネントはにサブスクライブします。 ]TaskContainerAppコンポーネントは次のようになります。次に例を示します。

App.js
// ...

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

export default App;

🥧パイのように簡単! これで、Reactアプリの状態を整理および管理するためのツールがもう1つあります。 あなたが手に入れるツールはあなた次第ですが、Unstatedは多くのユースケースにとってかなり良いオプションだと思います!