React学习笔记之十二:通过TodoList学Mobx

本文是关于MobX在React中的应用,详细介绍了如何通过TodoList实例学习MobX的使用,包括环境搭建、核心API如observable、action和computed的介绍,以及mobx-react的Provider、observer和inject的使用方法。文章最后提到了在实际项目中的角色和typescript的配合使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Mobx学习笔记


1. mobx的作用

官网描述
React 通过提供机制把应用状态转换为可渲染组件树并对其进行渲染。
MobX提供机制来存储和更新应用状态供 React 使用

简单点: React -> 把数据映射到页面上进行渲染
Mobx -> 提供存储和更新状态的方法(状态管理)

2. mobx-react 装饰器环境搭建

  • 创建一个react应用并添加mobx-react

create-react-app mobx
cd ./mobx
yarn install mobx-react mobx @babel/plugin-proposal-decorators-S

Notes:支持装饰器的环境搭建办法

  1. 本地不支持装饰器的话需要安装@babel/plugin-proposal-decorators, @babel/plugin-proposal-decorators主要是用了来支持装饰器的

  2. 接下来配置node_modules/babel-preset-create-app/create.js中的overrides为

overrides: [
  isFlowEnabled && {
    exclude: /\.tsx?$/,p
    plugins: [require("@babel/plugin-transform-flow-strip-types").default]
  },
  isTypeScriptEnabled && {
    test: /\.(tsx|js|jsx)?$/,
    plugins: [
      [
        require("@babel/plugin-proposal-decorators").default,
        { legacy: true }
      ]
    ]
  }
  1. 重启系统即可

3.mobx的基本使用方法

mobx 几个API介绍
  1. observable
  2. action
  3. computed
observable

observable将一个变量变为可以观测的变量, 被observable元素装饰后,该元素会变为可以观测的,当该元素改变时, 在页面上渲染的地方得到改变,但是通过observable中的元素,是readonly的,不能直接通过赋值来改变

使用方法:
class Store {
    @observable counter = 0;
}
action

如果想要改变observable变量的值,就需要通过一个action定义的方法来对其值进行改变(也可以异步)

使用方法:
class Store {
    // 略一些业务代码...
    @action
    IncreaseCounter() {
        this.counter++;
    }
}
computed

在Store中某些值是需要通过Store中的部分值通过计算而来,而computed可以在其依赖值变化的时候相应的发生变化

使用方法:
class Store {
    // 略一些业务代码...
    @computed
    get getDetailTimer() {
        return `${parseInt(this.counter / 3600)}Hour-${parseInt(this.counter / 3600 % 60)Min}`
    }
}

4.mobx-react基本使用方法

  1. Provider
  2. observer
  3. inject
1.Provider

Provider和React, Context中的Provider一个道理,将某一个值可以注入到组件树的深层中,一般会直接在App.js的外部加入Provider并将Store注入整个app中

2. observer

observer将整个组件变为可以观察的,Observer干的事情:一个收集器,将所有在Observer观察组件下的observable变量收集起来,然后当Store中有所监控到的obsevable变量发生变化的时候,就会重新执行render函数!!!

因此通过observer来使得组件进行动态渲染

使用方法:
@observer
class Store extends Component {
    // 略一些业务代码...
}
3.inject

inject通过将刚才provider提供的Store注入到特定的组件之中,注入之后就可以通过this.props.StoreName,StoreName为注入时的Store名

**一个坑:**要注意, inject和observer同时存在的时候,要先通过inject注入,再用observer进行监控

使用方法:
@inject('Store')
@observer
class Store extends Component {
    // 略一些业务代码...
    render() {
        const { Store } = this.props; // 获取注入的Store
    }
}

使用mobx的方法:

  1. 通过mobx构建公共的Store, 通过observable和action定义Store中的变量和方法(mobx)
  2. 在特定的地方生成Store实例, 并通过Provider来注入到组件树中 (mobx-react)
  3. 通过inject将Store注入到特定的组件中,并在组件中通过Store.param进行查询和通过Store.func实现状态的改变(mobx-react)

4. TodoList例子

  • mobx在react中的角色
  • mobx
  • mobx-react
  • typescript在mobx中的使用方法

具体的代码已在github上: React-todoList-typescript

0. mobx在react中的角色
  • mobx: 用于创建Store
  • mobx-react: 用于Store的注入和状态检测(将mobx连到react上)
1. mobx

// Store.ts

import { observable, action, computed } from "mobx";
import { appStoreState, todoState, todoItem } from "../Interface/Interface";

// appStoreState为AppStore的接口类
class AppStore implements appStoreState {
  // 监测的todoItem
  @observable todoItem = [{ id: 1, item: "Learn React", isDone: false }];
  // 用于增加todo条目
  @action
  addTodoItem(event: todoItem) {
    this.todoItem.push({
      ...event,
      id: this.id
    });
  }
  // 自动生成todoItem的id
  @computed get id() {
    return this.todoItem.length + 1;
  }
  // 自动计算未完成的项目数
  @computed get unsolvedNumber() {
    return this.todoItem.filter(elem => !elem.isDone).length;
  }
  // 删除一条错误数据
  @action
  deleteTodo(event: todoState) {
    this.todoItem.splice(
      this.todoItem.findIndex(elem => elem.id === event.id),
      1
    );
  }
  // 更改todo条目的状态
  @action
  changeEventStatus(event: todoState) {
    let target = this.todoItem.find(elem => elem.id === event.id);
    if (target) {
      target.isDone = !target.isDone;
    }
  }
}

export { AppStore };

2. mobx-react

// App.js
import React from "react";
import "./App.css";
import { Provider } from "mobx-react";
import { AppStore } from "./Store/Store";
import Todo from "./component/Todo";
import Controller from "./component/Controller";

// 创建一个mobx的Store实例
const appStore = new AppStore();

const App: React.FC = () => {
  return (
    // 通过Provider注入Store
    <Provider Store={appStore}>
      <div className="App">
        <Controller />
        <Todo />
      </div>
    </Provider>
  );
};

export default App;

通过mobx-react中的Provider来注入Store


// Todo.tsx
import { observer, inject } from "mobx-react";
import * as React from "react";
import { appStoreState, todoState } from "../Interface/Interface";
import "../scss/todo.scss";

// App的接口类
export interface IAppProps {
  Store?: appStoreState;
}

// 通过inject注入Store,这里注入的Store就是之前Provider传入的Store
// 通过observer来检测

@inject("Store")
@observer
export default class App extends React.Component<IAppProps> {
  changeStatus(todoItem: todoState) {
    const { Store } = this.props;
    if (Store) {
      Store.changeEventStatus(todoItem);
    }
  }

  removeItem(todoItem: todoState) {
    const { Store } = this.props;
    if (Store) {
      Store.deleteTodo(todoItem);
    }
  }

  renderTodoList = () => {
    const { Store } = this.props;
    if (Store) {
      console.log(Store.todoItem);
      return Store.todoItem.map((elem, index) => (
        <div className="list" key={`${index}`}>
          <div className="list-item">
            <input
              type="checkbox"
              id={`checkbox-${index}`}
              checked={elem.isDone}
              className="list-item__checkbox"
            />
            <span className="list-item__text">
              <span>{`${elem.id}-${elem.item}`}</span>
            </span>
          </div>
          <div className="list-control">
            <button onClick={this.removeItem.bind(this, elem)}>Delete</button>
            <button onClick={this.changeStatus.bind(this, elem)}>
              {elem.isDone ? "Cancel" : "Completed"}
            </button>
          </div>
        </div>
      ));
    }
  };

  public render() {
    const { Store } = this.props;
    return (
      <>
        {this.renderTodoList()}
        {Store && (
          <div className="total">
            Unsolved Numbers: <span>{Store.unsolvedNumber}</span>
          </div>
        )}
      </>
    );
  }
}

具体的使用方法已经大致整理在上面了~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值