React:mobx再学习(MobX 使用全流程)

445 阅读3分钟

B站-MobX 使用全流程
代码,atob('aHR0cHM6Ly9naXRlZS5jb20vaWZlcmNhcmx5L21vYng1')
本地项目,mobx-come,实践步骤:

环境搭建

  • 在本地创建react项目
    先创建目录 mobx-come ,然后再目录下执行npx create-react-app . template typescript
  • 按照视频,删除创建项目的src目录,拷贝资源src目录到项目上去,运行,ok

配置装饰器

  • 安装装饰器,为什么?yarn add -D react-app-rewired customize-cra @babel/plugin-proposal-decorators,还有其他的库可以取代这个库吗?
  • 在根目录创建文件config-overrides.js
const {override, addDecoratorsLegacy} = require('customize-cra');
module.exports = override(addDecoratorsLegacy());
  • 在根目录创建.babelrc
{
  "plugins": [
    ["@babel/plugin-proposal-decorators",{"legacy": true}]
  ]
}
  • 修改默认启动命令为
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
  • 测试装饰器,在index.js文件下添加测试代码
@foo
class Person{}

function foo(target){
    target.prototype.show = function (){
        console.log("hello, person");
    }
}
const p = new Person();
p.show(); // 控制台会打印出 hello, person

跑通mobx

  • 安装mobx yarn add mobx@5 mobx-react@5
  • 创建store文件夹
  • 创建3个store文件
// mainStore.js
class MainStore{
    constructor(rootStore) {
        this.rootStore = rootStore;
    }
}
// footStore.js
export default MainStore;
class FootStore{
    constructor(rootStore) {
        this.rootStore = rootStore;
    }
}
export default FootStore;
// index.js
import MainStore from "./mainStore";
import FootStore from "./footStore";
class RootStore{
    constructor() {
        this.FootStore = new FootStore(this);
        this.MainStore = new MainStore(this);
    }
}
  • 关联store与根节点
......
import { Provider } from "mobx-react";
import RootStore from "./store";

createRoot(document.querySelector('#root')).render(
    <Provider {...new RootStore()}>
        <App/>
    </Provider>
)
  • 测试mobx结果,在MainStore中写数据
@observable state = {
    list: [{
        id: 1,
        done: false,
        name: "吃饭"
    }, {
        id: 2,
        done: false,
        name: "睡觉"
    }]
}

其中,state有错误警告Experimental support for decorators is a feature that is subject to change in a future release. Set the 'experimentalDecorators' option in your 'tsconfig' or 'jsconfig' to remove this warning.
搜索,
根据https://stackoverflow.com/的提示,在tsconfig.js文件中增加

"emitDecoratorMetadata": true,
"experimentalDecorators": true

解决报错问题

  • 在组件中注入数据类
@inject("MainStore")
@observe()
export default class TodoMain extends Component {
	render() {
	  console.log(this.props.MainStore.state.list); // 成功获取数据
	  return (.....);
	}
}

模拟数据服务器

  • 在根目录创建data.json
{
  "todos": [
    {
      "id": 1,
      "done": false,
      "name": "吃饭"
    },
    {
      "id": 2,
      "done": false,
      "name": "睡觉"
    }
  ]
}
  • 安装json-server,yarn add json-server
  • 执行指令,模拟数据服务器,json-server data.json -p 8888

请求数据

  • 获取数据的注解,@action,如下
class MainStore {
    constructor(rootStore) {
        this.rootStore = rootStore;
    }

    @observable state = {
        list: [], // 删掉之前的数据,让初始数据为空
    };

    @action.bound getTodos(){
        // 请求网络,获取数据
    }
}
  • 安装网络请求库axiosyarn add axios
  • 封装网络请求,在src下创建目录utils,创建文件request.js,如下
import axios from "axios";
const request = axios.create({
    baseURL: "http://localhost:8888/todos",
    timeout: 5000,
})
export default request;
  • 回到MainStore,完成获取数据的方法
// 获取数据
@action.bound getTodos(){
    console.log("dsw","getTodos");
    request.get("/").then((res) => {
        console.log("dsw", res);
    })
}
  • 在组件中调用获取数据的方法
componentDidMount() {
  this.props.MainStore.getTodos();
}

getTodos中成功打印出数据,说明获取数据的方法,调用方法,都没有问题

  • 保存请求到的数据
const {data} = res;
this.state.list = data;
  • 在组件中使用数据,渲染数据
const {state} = this.props.MainStore;
const {list} = state || {};
// list就是被渲染的数据,在UI中显示该list

classnames包

  • 安装yarn add classnames
  • 使用
...
{list.map((item) => (
    <li className={classNames({
        complete: item.done,
    })} key={item.id}>
        <div className='view'>
            <input className='toggle' type='checkbox' checked={item.done} onChange={() => {
            }}/>
            <label>{item.name}</label>
            <button className='destroy'></button>
        </div>
        <input className='edit' value='Create a TodoMVC template' onChange={() => {
        }}/>
    </li>))}
...

修改数据的几种方式

使用严格模式时configure({enforceActions: "observed"}),不能直接在mainStore中修改数据,有几种间接修改的方法

  • 通过set的方法,推荐写法
// 获取数据
@action.bound getTodos() {
    console.log("dsw", "getTodos");
    request.get("/").then((res) => {
        this.setTodos(res);
    })
}
// 修改数据
@action.bound setTodos(res) {
    const {data} = res;
    this.state.list = data;
}
  • 通过runInActions
// 获取数据
@action.bound getTodos() {
    console.log("dsw", "getTodos");
    request.get("/").then((res) => {
        runInAction(() => {
            const {data} = res;
            this.state.list = data;
        })
    })
}
  • 通过action方法
// 获取数据
@action.bound getTodos() {
    console.log("dsw", "getTodos");
    request.get("/").then((res) => {
        action("@_@",() => {
            const {data} = res;
            this.state.list = data;
        })();
    })
}

删除数据的方式

// 删除数据
@action.bound async delTodos(id){
    await request.delete(`/${id}`)
    await this.getTodos();
}

更新数据的方式

// 更新数据
@action.bound
async updateTodos(id, key, value) {
    await request.patch(`/${id}`, {[key]: value});
    await this.getTodos();
}

添加数据的方式

// 添加数据
@action.bound
async addTodos(name){
    console.log("dd-addTodos");
    await request.post(`/`, {name, done: false});
    await this.getTodos();
}

全选与取消全选

// 获取选中状态
@computed get getMainRadioStatus() {
    return this.state.list.every((item) => item.done);
}

// 批量改变状态
@action.bound
async updatePerRadioStatus(done){
    const promiseAll = this.state.list.map((item) => {
        return this.updateTodos(item.id, 'done', done);
    })
    await Promise.all(promiseAll);
    await this.getTodos();
}

其他功能(如,编辑Item,切换tab等,略)

改造为Mobx6的配置

  • 安准mobx6
    yarn add mobx mobx-react // 让react安装mobx的包
    yarn add mobx mobx-react-lite // lite表示用于函数组件的,让react安装mobx的包
  • 卸载原有mobx
    yarn remove mobx-react
  • 改造代码