从0开始学习MobX

371 阅读2分钟

MobX学(踩)习(坑)指(日)南(记)

一、引言

最近换工作,新公司的项目中使用React+MobX,但我之前只用过React+Redux,所以呢,只能继续学了...

简单过完一遍mobx文档,觉得核心概念已经懂了,心想“啥都不是,这也不难么”,然后就掉到了坑里,爬了好久才出坑。

特此记录踩坑经验,以期帮助大家不踩同样的坑。

二、配置项目环境

按照官方文档,我使用了以下命令,用cra命令创建了项目,然后修改config配置,让项目支持修饰符,接着安装了mobxmobx-react,在准备开撸代码的时候发现了问题。

创建项目+安装依赖

// 创建项目
npx create-react-app mobx-app

// 安装依赖
npm i mobx mobx-react
npm i --save-dev @babel/core @babel/plugin-proposal-decorators @babel/preset-env customize-cra react-app-rewired

配置Babel

// .babelrc
{
    "presets": [
        "@babel/preset-env"
    ],
    "plugins": [
        [
            "@babel/plugin-proposal-decorators",
            {
                "legacy": true
            }
        ]
    ]
}

配置支持修饰符

// config-overrides.js
const { override, addDecoratorsLegacy } = require('customize-cra');
module.exports = override(
    addDecoratorsLegacy(),
)

配置lint检验

// jsconfig.json
{
  "compilerOptions": {
    "jsx": "react",
    "baseUrl": ".",
    "experimentalDecorators": true,
  },
  "include": ["src/**/*"]
}

三、开始code

// src/App.js

import TodoListView from './pages/TodoList';

function App() {
  return (
    <TodoListView/>
  );
}

export default App;
// src/pages/TodoList.js

import { observer } from "mobx-react";
import React from "react";
import { TodoList } from "src/store/todo";

const store = new TodoList();

@observer
class TodoListView extends React.Component {
    constructor(props) {
        super(props);
        this.state={
            toAddTodoText: ""
        }
    }
    render() {
        const { todoList,addTodo,switchTodo } = store;
        return(
            <div>
                <ul>
                    {todoList.map((todo) => (
                        <li key={todo.id}>
                            <h1>{todo.title}</h1>
                            <input type={"checkbox"} checked={todo.finished} onClick={() => {
                                switchTodo(todo.id)
                            }}/>finished
                        </li>
                    ))}
                </ul>
                <div>total: {store.todoListLength}</div>
                <div>finished: {store.finishedNums}</div>
                <div>
                    <input type={"text"} placeholder="please input a todo title" onChange={(e) => {
                        this.setState({
                            toAddTodoText: e.target.value
                        })
                    }}/>
                    <button onClick={() => {
                        addTodo(this.state.toAddTodoText)
                    }}>add Todo</button>
                </div>
            </div>
        )
    }
}

export default TodoListView;
// src/store/todo.js

const { observable, computed, action } = require("mobx");

class Todo {
    constructor(){
        makeObservable(this);
    }
    id = Math.random();
    @observable title = "";
    @observable finished = false;
}

class TodoList {
    @observable todoList = [];
    @computed get finishedNums() {
        return this.todoList.filter((todo) => todo.finished).length;
    }
    @computed get todoListLength() {
        return this.todoList.length;
    }
    @action.bound
    addTodo(todoTitle) {
        const newTodo = new Todo();
        newTodo.title = todoTitle;
        this.todoList.push(newTodo);
    }
    @action.bound
    switchTodo(id) {
        const targetTodo = this.todoList.find((todo) => todo.id === id);
        targetTodo.finished = !targetTodo.finished;
    }
}

export {
    Todo,
    TodoList,
}

yarn start启动项目后,在输入框输入一个要新增的todo title,结果发现和预期不一致:UI没有更新,新的todo没有被添加

打开控制台有报错:todo.js:27 Uncaught TypeError: Cannot read properties of undefined (reading 'todoList')

四、问题定位及解决

经过各种定位与排查,最终找到了原因:mobx版本

还记得我们之前提到的mobx文档吗?目前搜索到的mobx文档主要是5.x版本,而我们npm i mobx的时候,安装的是6.x版本。mobx6.x文档中指出:我们必须使用makeObservable来使得对象可观察,换句话说,如果我们不使用makeObservablemakeAutoObservable的话,mobx就无法观察到我们使用@observable定义的数据,我们就无法正常使用mobx。