mobx 在 react 中的 类组件、函数组件、配合 hooks 的使用

4,660 阅读1分钟

前期准备

使用 react-app-rewired 修改 create-react-app 的配置, 使其支持装饰器语法

  • 安装依赖
yarn add react-app-rewired
yarn add --dev customize-cra
yarn add @babel/plugin-proposal-decorators
  • config-overrides.js
const {
  override, 
  addDecoratorsLegacy, 
  disableEsLint
} = require('custommize-cra');
module.exports = override(
  addDecoratorsLegacy(),
  disableEsLint()
);
  • package.json
"scripts": {
  "start": "react-app-rewired start",
  "build": "react-app-rewired build",
  "test": "react-app-rewired test",
  "eject": "react-app-rewired eject"
}
  • 处理 vscode 报错

图片

"javascript.implicitProjectConfig.experimentalDecorators": true

安装 mobx

yarn add mobx
yarn add mobx-react

创建 store

import {observable, action, computed} from 'mobx';
const INIT_TODO = {
    id: 1,
    text: 'mobx事例',
    completed: false
}
class TodoStore {
    @observable 
    todoList = [INIT_TODO];
    
    @computed 
    get totalTodos() {
        return this.todoList.length
    }
    
@action 
    addTodo(text){
        const newTodo = {
            id: +new Date(),
            text,
            completed: false
        }
        this.todoList = [...this.todoList, newTodo];
    }
    @action
    changeStatus(id) {
        this.todoList = this.todoList.map(item => {
            return item.id === id
            ? {...item, completed: !item.completed}
            : item
        })
    }
    @action 
    delTodo(id){
        this.todoList = this.todoList.filter(item => item.id !== id)
    }
    @action
    clearTodos() {
        this.todoList = [];
    }
}
export default TodoStore;

Provider 提供容器

import {Provider} from 'mobx-react';
import stores from './store';
<Provider {...stores} > 
    <div className="App">
         <Input 
            value={value}
            placeholder="添加代办"
            onPressEnter={handleSubmit}
            onChange={e => setValue(e.target.value)}
         />
         <TodoList />
         <Footer />
     </div>
</Provider>

在类组件中使用

import {inject, observer} from 'mobx-react';
import React,{PureComponent} from 'react';
import Item from './item';
@inject('todoStore')
@inject('globalStore')
@observer
class TodoList extends PureComponent {
    handleDel = (id) => {
        this.props.todoStore.delTodo(id);
    }
    render() {
        const {todoStore: {todoList, totalTodos}} = this.props;
        return (
            <div>
                {
                    totalTodos 
                    ? <>
                        {
                            todoList.map(item => (
                                <Item key={item.id} data={item}/>
                            ))
                        }
                    </>
                    : <div className="empty">今日事今日毕</div>
                }
            </div>
        )
    }
}
export default TodoList;

在函数组件中使用

import { inject, observer } from 'mobx-react';
import React from 'react';
function Item({todoStore, data}) {
   return (
        <div >
            <p>
                <span>
                    {data.text} 
</span>
                <span>
                    <span 
                        className="pointer"
                        onClick={_ => todoStore.delTodo(data.id)}
                    >
                        删除
                    </span>
                </span>
            </p>
        </div>
    )
}
export default inject('todoStore','globalStore')(observer(Item));

结合 hooks 使用

  • 安装 mobx-react-lite
yarn add mobx-react-lite
  • 创建 context
import React from 'react';
import stores from '../store';
export const StoresContext = React.createContext({
    ...stores
})
  • 创建 context hooks
import React from 'react'
import { StoresContext } from '../contexts';
export const useStores = () => React.useContext(StoresContext)
  • 在组件中使用
import React from 'react';
import { useStores } from '../hooks';
import { useObserver } from 'mobx-react-lite';
function Footer() {
    let store = useStores(); 
    const {todoStore} = store;
    return useObserver(() => (
        <div className="footer">
            count: {todoStore.totalTodos}
            <span className="pointer" onClick={_ => todoStore.clearTodos()} >重置</span>
        </div>
    ))
}
export default Footer;

完整的例子