另一天,在React!中管理应用程序状态的另一种方法是(https://github.com/jamiebuilds/unstated)使用React的新 背景API来管理您的状态的新库。
让我们来看看如何使用Unstated,不要担心,它将是完全无痛的!
安装
只需使用 npm 或 Yarn 安装未注明的包:
1$ npm install unstated
2
3# or
4$ yarn add unstated
使用
Unstated为我们提供了三种内容:一个集装箱类、一个订阅组件和一个提供商组件:
集装箱
您可以通过扩展 Unstated 的集装箱类来创建您的状态的一块集装箱:
1[label ./containers/todo.js]
2import { Container } from 'unstated';
3
4class TodoContainer extends Container {
5 // ...
6}
7
8export default TodoContainer;
在容器中,您可以使用状态和 setState 来管理状态,正如您在 React 中对本地组件状态所做的那样:
1[label ./containers/todo.js]
2import { Container } from 'unstated';
3
4class TodoContainer extends Container {
5 state = {
6 todos: []
7 };
8
9 id = 0;
10
11 addTodo = todo => {
12 const newTodo = { id: this.id++, marked: false, description: todo };
13 this.setState({
14 todos: [...this.state.todos, newTodo]
15 });
16 };
17
18 removeTodo = id => {
19 this.setState({
20 todos: this.state.todos.filter(todo => todo.id !== id)
21 });
22 };
23
24 markTodo = id => {
25 this.setState({
26 todos: this.state.todos.map(todo => {
27 if (todo.id !== id) {
28 return todo;
29 } else {
30 return { ...todo, marked: !todo.marked };
31 }
32 })
33 });
34 };
35}
36
37export default TodoContainer;
正如你所看到的,我们所有的商业逻辑都包含在集装箱内。我们的React组件将能够从这个集装箱中订阅状态,并通过调用集装箱中定义的方法来更改状态。
供应商
提供者组件用于存储容器实例,并允许其子女订阅这些实例. 只需在顶级使用它,围绕将订阅容器的组件:
1[label App.js]
2import React, { Component } from 'react';
3import { Provider } from 'unstated';
4
5import Todos from './Todos';
6import AddTodo from './AddTodo';
7
8class App extends Component {
9 render() {
10 return (
11 <Provider>
12 <AddTodo />
13 <Todos />
14 </Provider>
15 );
16 }
17}
18
19export default App;
签名
最后,Unstated的订阅
组件采用一个集合集装箱的组件,并预期一个作为其子女的组件的组件(见 render prop)将收到每个订阅的集装箱的实例。
用我们的例子,我们的 AddTodo 组件可以看起来像这样:
1[label AddTodo.js]
2import React from 'react';
3import { Subscribe } from 'unstated';
4
5import TodoContainer from './containers/todo';
6
7class AddTodo extends React.Component {
8 inputRef = React.createRef();
9
10 handleClick = addTodo => {
11 if (this.inputRef.current.value) {
12 addTodo(this.inputRef.current.value);
13 this.inputRef.current.value = '';
14 }
15 };
16
17 render() {
18 return (
19 <div>
20 <input type="text" placeholder="your new todo" ref={this.inputRef} />
21
22 <Subscribe to={[TodoContainer]}>
23 {todoContainer => (
24 <button onClick={() => this.handleClick(todoContainer.addTodo)}>
25 Add
26 </button>
27 )}
28 </Subscribe>
29 </div>
30 );
31 }
32}
33
34export default AddTodo;
注意如何访问一个Todo容器实例,并可以调用它的addTodo
方法. 在这里,我们还使用React的 新创建Ref API。
让我们在我们的 Todos 组件中使用 Unstated 的订阅组件来订阅我们的 todo 容器,显示包含在其状态中的 todos,并允许将其标记为完成或删除它们:
1[label Todos.js]
2import React from 'react';
3import { Subscribe } from 'unstated';
4
5import TodoContainer from './containers/todo';
6
7class Todos extends React.Component {
8 render() {
9 return (
10 <ul>
11 <Subscribe to={[TodoContainer]}>
12 {todoContainer =>
13 todoContainer.state.todos.map(todo => (
14 <li key={todo.id}>
15 <span
16 className={todo.marked ? 'marked' : null}
17 onClick={() => todoContainer.markTodo(todo.id)}
18 >
19 {todo.description}
20 </span>
21 <button onClick={() => todoContainer.removeTodo(todo.id)}>
22 X
23 </button>
24 </li>
25 ))
26 }
27 </Subscribe>
28 </ul>
29 );
30 }
31}
32
33export default Todos;
多种集装箱
将不同的状态分离成自己的单独容器同样容易。比如说,我们的应用现在也需要处理任务,让我们把我们的 AddTodo 组件重构成一个 AddItem 组件,该组件也订阅到 TaskContainer。
我们将使用几个无线电按钮让用户选择添加一个Todo或一个任务,我们还将更改我们的文本输入元素为一个控制的输入,而不是使用一个ref:
1[label AddItem.js]
2import React from 'react';
3import { Subscribe } from 'unstated';
4
5import TodoContainer from './containers/todo';
6import TaskContainer from './containers/task';
7
8class AddItem extends React.Component {
9 state = {
10 itemValue: '',
11 itemChoice: 'todo'
12 };
13
14 handleInputChange = e => {
15 this.setState({
16 itemValue: e.target.value
17 });
18 };
19
20 handleRadioChange = e => {
21 this.setState({
22 itemChoice: e.target.value
23 });
24 };
25
26 handleClick = (addTodo, addTask) => {
27 if (this.state.itemValue && this.state.itemChoice === 'todo') {
28 addTodo(this.state.itemValue);
29 } else if (this.state.itemValue) {
30 addTask(this.state.itemValue);
31 }
32
33 this.setState({
34 itemValue: ''
35 });
36 };
37
38 render() {
39 return (
40 <div>
41 <input
42 type="text"
43 placeholder="your new item"
44 value={this.state.itemValue}
45 onChange={this.handleInputChange}
46 />
47
48 <input
49 type="radio"
50 id="todoItem"
51 name="itemType"
52 value="todo"
53 checked={this.state.itemChoice === 'todo'}
54 onChange={this.handleRadioChange}
55 />
56 <label htmlFor="todoItem">Todo</label>
57
58 <input
59 type="radio"
60 id="taskItem"
61 name="itemType"
62 value="task"
63 checked={this.state.itemChoice === 'task'}
64 onChange={this.handleRadioChange}
65 />
66 <label htmlFor="taskItem">Task</label>
67
68 <Subscribe to={[TodoContainer, TaskContainer]}>
69 {(todoContainer, taskContainer) => (
70 <button
71 onClick={() =>
72 this.handleClick(todoContainer.addTodo, taskContainer.addTask)
73 }
74 >
75 Add {this.state.itemChoice}
76 </button>
77 )}
78 </Subscribe>
79 </div>
80 );
81 }
82}
83
84export default AddItem;
现在我们可以在组件树内分离显示我们的 todos和任务,Todo组件订阅TodoContainer,TaskContainer订阅TaskContainer的任务组件。
1[label App.js]
2// ...
3
4class App extends Component {
5 render() {
6 return (
7 <Provider>
8 <AddItem />
9 <Todos />
10 <Tasks />
11 </Provider>
12 );
13 }
14}
15
16export default App;
简单的蛋糕! 现在你有另一个工具在你的武器库,当它涉及到组织和管理你的React应用程序的状态。