前端笔记

分享前端开发思考与感悟

具有React和TypeScript的MobX

MobX是流行的状态管理库之一。MobX的一大优点是,我们可以将状态存储在简单的数据结构中,并允许库负责保持所有内容为最新状态。MobX API非常简单;实际上,它的核心是以下四个简单的构建块:

  • 可观察的
  • 动作
  • 已计算
  • 反应

可观察的

想法是,当数据更改时,可观察对象会通知观察者。要将属性定义为可观察的,我们要做的就是使用@observable装饰器:

class TodoStore {
  @observable todos: Todo[]
}

现在,当将新值分配给todos数组时,将触发通知,并且将通知所有关联的观察者。

动作

动作是更改可观察对象(更新状态)的一种方法。为了定义动作,我们在商店内部使用@action以下方法装饰方法:

@action toggleTodo = (id: string) => {
    this.todos = this.todos.map(todo => {
      if (todo.id === id) {
        return {
          ...todo,
          completed: !todo.completed
        };
      }
      return todo;
    });
};

已计算

可使用计算来从现有状态或其他计算值中得出值:

@computed get info() {
    return {
      total: this.todos.length,
      completed: this.todos.filter(todo => todo.completed).length,
      notCompleted: this.todos.filter(todo => !todo.completed).length
    };
}

反应

反应从商店内部跟踪可观察到的东西。在下面的示例中,如果todos调用了要设置的动作,则它将运行第二个参数。

class TodoStore {
  constructor() {
    reaction(
      () => this.todos,
      _ => console.log(this.todos.length)
    );
  }

使用MobX和React创建一个简单的Todo应用

现在我们已经讨论了主要概念,让我们使用React,MobX和TypeScript创建一个简单的todo应用程序:

《具有React和TypeScript的MobX》

因此,转到终端,创建一个目录,然后将其放入CD,然后键入以下命令以构建新的React项目:

npx create-react-app . --typescript

对于这个项目,我正在使用Bootstrap,因此让我们将其添加为新创建项目的依赖项:

yarn add bootstrap --save

现在转到index.tsx并导入bootstrap.css

import "bootstrap/dist/css/bootstrap.css"

现在,我们将安装所需的依赖项:

yarn add mobx mobx-react-lite uuid @types/uuid --save

我们要做的下一件事就是设置experimentalDecorators标志,truetsconfig.json为了使MobX装饰正确地编译:

{
  "compilerOptions": {
    // other stuff...

    "experimentalDecorators": true
  }
}

完成以上所有步骤后,我们就可以开始使用MobX。现在让我们在项目内部创建一个商店src/stores/TodoStore.ts。向其添加以下代码:

import { observable, action, computed, reaction } from "mobx"
import { createContext } from "react"
import uuidv4 from "uuid/v4"

export interface Todo {
  id?: string;
  title: string;
  completed: boolean;
}

class TodoStore {
  constructor() {
    reaction(() => this.todos, _ => console.log(this.todos.length))
  }

  @observable todos: Todo[] = [
    { id: uuidv4(), title: "Item #1", completed: false },
    { id: uuidv4(), title: "Item #2", completed: false },
    { id: uuidv4(), title: "Item #3", completed: false },
    { id: uuidv4(), title: "Item #4", completed: false },
    { id: uuidv4(), title: "Item #5", completed: true },
    { id: uuidv4(), title: "Item #6", completed: false },
  ]

  @action addTodo = (todo: Todo) => {
    this.todos.push({ ...todo, id: uuidv4() })
  }

  @action toggleTodo = (id: string) => {
    this.todos = this.todos.map(todo => {
      if (todo.id === id) {
        return {
          ...todo,
          completed: !todo.completed,
        }
      }
      return todo
    })
  }

  @action removeTodo = (id: string) => {
    this.todos = this.todos.filter(todo => todo.id !== id)
  }

  @computed get info() {
    return {
      total: this.todos.length,
      completed: this.todos.filter(todo => todo.completed).length,
      notCompleted: this.todos.filter(todo => !todo.completed).length,
    }
  }
}

export default createContext(new TodoStore())

现在,在src目录中创建一个名为components的新文件夹,并添加TodoAdd.tsxTodoList.tsx

待办事项

import React, { useContext, useState } from "react"
import TodoStore from "../stores/TodoStore"
import { observer } from "mobx-react-lite"

const AddTodo = () => {
  const [title, setTitle] = useState("")
  const todoStore = useContext(TodoStore)
  const { addTodo, info } = todoStore

  return (
    <>
      <div className="alert alert-primary">
        <div className="d-inline col-4">
          Total items: &nbsp;
          <span className="badge badge-info">{info.total}</span>
        </div>
        <div className="d-inline col-4">
          Finished items: &nbsp;
          <span className="badge badge-info">{info.completed}</span>
        </div>
        <div className="d-inline col-4">
          Unfinished items: &nbsp;
          <span className="badge badge-info">{info.notCompleted}</span>
        </div>
      </div>
      <div className="form-group">
        <input
          className="form-control"
          type="text"
          value={title}
          placeholder="Todo title..."
          onChange={e => setTitle(e.target.value)}
        />
      </div>
      <div className="form-group">
        <button
          className="btn btn-primary"
          onClick={_ => {
            addTodo({
              title: title,
              completed: false,
            })
            setTitle("")
          }}
        >
          Add Todo
        </button>
      </div>
    </>
  )
}

export default observer(AddTodo)

待办事项清单

import React, { useContext } from "react";
import TodoStore from "../stores/TodoStore";
import { observer } from "mobx-react-lite";

const TodoList = () => {
  const todoStore = useContext(TodoStore);
  const { todos, toggleTodo, removeTodo } = todoStore;
  return (
    <>
      <div className="row">
        <table className="table table-hover">
          <thead className="thead-light">
            <tr>
              <th>Title</th>
              <th>Completed?</th>
              <th>Actions</th>
            </tr>
          </thead>
          <tbody>
            {todos.map(todo => (
              <tr key={todo.id}>
                <td>{todo.title}</td>
                <td>{todo.completed ? "" : ""}</td>
                <td>
                  <button
                    className="btn btn-sm btn-info"
                    onClick={_ => toggleTodo(todo.id!)}
                  >
                    Toggle
                  </button>
                  <button
                    className="btn btn-sm btn-danger"
                    onClick={_ => removeTodo(todo.id!)}
                  >
                    Remove
                  </button>
                </td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    </>
  );
};

export default observer(TodoList);

这两个组件都使用HOCobserver作为HOC,以使我们的商店成为组件的观察者。因此,对任何可观察对象的任何更改都将导致React组件重新呈现。

就是这样🚀您现在就可以在React应用程序中使用MobX。

点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注