我们相信:世界是美好的,你是我也是。平行空间的世界里面,不同版本的生活也在继续...

还是接着分析mobx官方教程,本文描述官方的React版本的todoList。那么,mobx是如何驱动react组件里面的数据的呢?如何让每个引用都变得Reactive的呢?这就是本文要讨论的问题。

苏南大叔:Mobx状态管理,如何实现 Mobx + React 版本的 todoList ? - mobx-react-todolist
Mobx状态管理,如何实现 Mobx + React 版本的 todoList ?(图4-1)

苏南大叔的“程序如此灵动”博客,记录苏南大叔的代码编程经验总结。本文测试环境:nodejs@20.18.0create-react-app@5.0.1react@18.3.1mobx@6.13.5。当react碰到了mobx,就产生了本文的写在背景信息。

前文回顾

普通非React版本的todoListMobx官方教程的修改版,参考如下:

本文的例子中,使用props传递store,使用了useCallback包装传递函数。参考文章:

苏南大叔:Mobx状态管理,如何实现 Mobx + React 版本的 todoList ? - todolist-ui
Mobx状态管理,如何实现 Mobx + React 版本的 todoList ?(图4-2)

Store

这个Store.js和原来没有React的时候,代码基本一致,其实就是一个被Mobx观察过的普通类。基于官方的代码,略有改动。

import { makeObservable, observable, computed, action, autorun } from "mobx";
class TodoStore {
  todos = [];
  pendingRequests = 0;
  constructor() {
    makeObservable(this, {
      todos: observable,
      pendingRequests: observable,
      completedTodosCount: computed,
      report: computed,
      addTodo: action,
      addTodoSync: action,
      modify: action,
    });
    // 这是另外一个函数包装 autorun()
    autorun(() => console.log(this.report));
  }
  get completedTodosCount() {
    return this.todos.filter((todo) => todo.completed === true).length;
  }
  get report() {
    if (this.todos.length === 0) return "";
    const nextTodo = this.todos.find((todo) => todo.completed === false);
    const percent = `进度:${this.completedTodosCount}/${this.todos.length}`;
    return percent + `,准备:${nextTodo ? nextTodo.name : "<无>"}`;
  }
  addTodo(name) {
    if (!name || name.trim() === "") {
      return;
    }
    this.todos.push({
      name,
      completed: false,
    });
  }
  addTodoSync(name, timeout = 1000) {
    if (!name || name.trim() === "") {
      return;
    }
    this.pendingRequests++;
    let that = this;
    setTimeout(
      action(() => {
        that.addTodo(name);
        that.pendingRequests--;
      }),timeout
    );
  }
  modify(id, info) {
    this.todos[id] = { ...this.todos[id], ...info };
  }
}
window.store = new TodoStore();
export default window.store;

这里把store挂在window.上的主要目的,还是想留着直接在f12里面调试。

苏南大叔:Mobx状态管理,如何实现 Mobx + React 版本的 todoList ? - mobx-action
Mobx状态管理,如何实现 Mobx + React 版本的 todoList ?(图4-3)

这个代码中,增加了两个action

  • 一个是对官方的额外远程加载todo的代码封装addTodoSync()
  • 另外一个是对todo自身字段修改的函数modify()

布局相关逻辑

App.js

import { observer } from "mobx-react";
import React, { useCallback } from "react";
import "./App.css";
import RenderCount from "./RenderCount";
import store from "./Store";
const TodoItem = observer(({ modify, id, todo }) => {
  console.log("item", id, todo);
  const onToggleCompleted = () => {
    modify(id, { completed: !todo.completed });
  };
  const onRename = () => {
    modify(id, { name: prompt("任务名称", todo.name) || todo.name });
  };
  return (
    <li onDoubleClick={onRename}>
      <input
        type="checkbox"
        checked={todo.completed}
        onChange={onToggleCompleted}
      />
      {todo.name}
      <RenderCount />
    </li>
  );
});
const TodoLayout = observer(({ store }) => {
  const onNewTodo = () => {
    let tmp = (Math.random() * 100).toFixed(0);
    console.log(tmp, tmp % 2);
    if (tmp % 2 == 0) {
      store.addTodo(prompt("输入新的待办:", "请来杯咖啡"));
    } else {
      store.addTodoSync(prompt("输入新的待办:", "请来瓶红牛"));
    }
  };
  const fn = useCallback((id, info) => store.modify(id, info), []);
  // const fn = (id, info) => store.modify(id, info);
  return (
    <div>
      {store.report}
      <ul>
        {store.todos.map((todo, id) => (
          <TodoItem key={id} modify={fn} id={id} todo={todo} />
        ))}
      </ul>
      {store.pendingRequests > 0 ? <marquee>正在加载……</marquee> : null}
      <button onClick={onNewTodo}>新待办</button>
      <small>(双击待办进行编辑)</small>
    </div>
  );
});
const App = () => {
  return (
    <>
      <TodoLayout store={store}></TodoLayout>
    </>
  );
};
export default App;

这里调用了RenderCount组件,参考前文:

这部分代码相比较官方代码,也做了一些修改。这里想说的是:react循环一个子组件的时候,添加的key是无法被子组件的props所获取的。

苏南大叔:Mobx状态管理,如何实现 Mobx + React 版本的 todoList ? - observer-mobx
Mobx状态管理,如何实现 Mobx + React 版本的 todoList ?(图4-4)

高阶组件 observer()

这里带来了一个新的概念,高阶组件observer(),它来自mobx-react 或者 mobx-react-lite

使用方式是:通过observer()包裹一个React组件。用于将React组件连接到MobX状态树,将React组件转化为响应式组件。

调用方式如下:

import { observer } from "mobx-react";

因此,需要安装两个第三方库了。

npm install mobx mobx-react --save

或者

npm install mobx mobx-react-lite --save

mobx-reactmobx-react-lite中都有observer,后者的observer不支持将类组件转换成响应式组件,但是拥有更小的体积。

结语

更多mobx相关文章,可以参考苏南大叔的博客:

如果本文对您有帮助,或者节约了您的时间,欢迎打赏瓶饮料,建立下友谊关系。
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。

 【福利】 腾讯云最新爆款活动!1核2G云服务器首年50元!

 【源码】本文代码片段及相关软件,请点此获取更多信息

 【绝密】秘籍文章入口,仅传授于有缘之人   react