MobX专题

源码地址:https://github.com/qifutian/learngit/tree/main/mobx/mobx-guide

  1. MobX简介
  2. 开发准备
  3. MobX + react
  4. MobX异步
  5. MobX数据监测
  6. 综合案例

MobX简介

简单, 可扩展的状态管理库
MobX 是由 Mendix,Coinbase,Facebook 开源和众多个人赞助商所赞助的
React 和 MobX 是一对强力组合,React 负责渲染应用的状态,MobX 负责管理应用状态供 React 使用

浏览器支持

MobX 5 版本运行在任何支持 ES6 proxy 的浏览器,不支持 IE11,Node.js 6
MobX 4 可以运行在任何支持 ES5 的浏览器上
MobX 4 和 5的 API 是相同的

开发准备

方法一:启用装饰器语法支持
安装babel插件

  1. 弹射项目底层配置:npm run eject
  2. 下载装饰器语法babel插件:npm install @babel/plugin-proposal-decorators
  3. 在 package.json 文件中加入配置

MobX专题_第1张图片
方法二:启用装饰器语法支持

  1. npm install react-app-rewired customize-cra @babel/plugin-proposal-decorators
  2. 在项目根目录下创建 config-overrides.js 并加入配置

MobX专题_第2张图片
3. 更改 package.json 文件中的项目启动配置

MobX专题_第3张图片

解决 vscode 编辑器关于装饰器语法的警告

修改配置:"javascript.implicitProjectConfig.experimentalDecorators": true

MobX 使用

  1. 下载MobX ,npm install mobx mobx-react
  2. 工作流程
    在这里插入图片描述
  3. 创建项目 create-react-app mobx-guide
  4. 安装依赖 npm i mobx mobx-react
  5. 在src下常见stores文件夹,放置mobx相关代码,新建counterStore.js
  6. 定义store类MobX专题_第4张图片
    7.创建 Store 对象,通过 Provider 组件将 Store 对象放置在全局
    MobX专题_第5张图片
  7. 将 Store 注入组件,将组件变成响应式组件
    MobX专题_第6张图片
// 1. 创建store对象 存储默认状态0
// 2. 将store对象放在一个全局的 组件可以够的到的地方
// 3. 让组件获取store对象中的状态 并将状态显示在组件中

class CounterStore {
     
   count = 0;
}
const counter = new CounterStore();

export default counter;

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import {
      Provider } from 'mobx-react';
import App from './App';
import counter from './stores/counterStore';

ReactDOM.render(
  <Provider counter={
     counter}><App /></Provider>,
  document.getElementById('root')
);

mobx修改

// 1. 创建store对象 存储默认状态0
// 2. 将store对象放在一个全局的 组件可以够的到的地方
// 3. 让组件获取store对象中的状态 并将状态显示在组件中
import {
      observable, configure, action, runInAction, flow, computed, autorun } from 'mobx';

// 通过配置强制程序使用action函数更改应用程序中的状态
configure({
     enforceActions: 'observed'});
class CounterStore {
     
   @observable count = 0;
   @action increment () {
     
    this.count = this.count + 1;
  }

  @action decrement () {
     
    this.count = this.count - 1;
  }
   
}
const counter = new CounterStore();

export default counter;

修改类中普通函数的this指向

// 1. 创建store对象 存储默认状态0
// 2. 将store对象放在一个全局的 组件可以够的到的地方
// 3. 让组件获取store对象中的状态 并将状态显示在组件中
import {
      observable, configure, action, runInAction, flow, computed, autorun } from 'mobx';

// 通过配置强制程序使用action函数更改应用程序中的状态
configure({
     enforceActions: 'observed'});
class CounterStore {
     
   @observable count = 0;
   @action.bound increment () {
     
    this.count = this.count + 1;
  }

  @action.bound decrement () {
     
    this.count = this.count - 1;
  }
   
}
const counter = new CounterStore();

export default counter;

异步更新组件方式
引入axios

  @observable count = 0;
  @observable users = [];
     @action.bound increment () {
     
    this.count = this.count + 1;
  }

  @action.bound decrement () {
     
    this.count = this.count - 1;
  }

   @action.bound getData = flow(function* () {
     
    let {
      data } = yield axios.get('https://api.github.com/users');
    this.users = data
  }).bind(this)

调用时候报错,需要用一个方法包裹,引入runInAction

  // @action.bound async getData () {
     
  //   let { data } = await axios.get('https://api.github.com/users');
  //   runInAction(() => this.users = data);
  // }

或使用flow方法

  // getData = flow(function* () {
     
  //   let { data } = yield axios.get('https://api.github.com/users');
  //   this.users = data
  // }).bind(this)

Mobx数据监测

computed计算值

计算值是可以根据现有的状态或其它计算值衍生出的值
在复杂组件使用,将复杂的业务逻辑从模板中进行抽离

案例
MobX专题_第7张图片

  @computed get getResult () {
     
    return this.count * 10
  }

禁止普通函数更改程序状态并引入action装饰器

工作流程
在这里插入图片描述

在视图通过action函数修改程序状态,当状态发生更改之后,在将状态同步给视图

当普通函数修改状态时候,不好监测
应该只有action函数能修改

通过配置方式,configure方法

// 通过配置强制程序使用action函数更改应用程序中的状态
configure({
     enforceActions: 'observed'});

数据监测 autorun方法

当监测的状态发生变化时,你想根据状态产生 “效果”,请使用 autorun。
autorun 会在初始化的时候执行一次,会在每次状态发生变化时执行。

MobX专题_第8张图片
源码

// 1. 创建store对象 存储默认状态0
// 2. 将store对象放在一个全局的 组件可以够的到的地方
// 3. 让组件获取store对象中的状态 并将状态显示在组件中
import {
      observable, configure, action, runInAction, flow, computed, autorun } from 'mobx';
import axios from 'axios';

// 通过配置强制程序使用action函数更改应用程序中的状态
configure({
     enforceActions: 'observed'});

class CounterStore {
     

  constructor () {
     
    autorun (() => {
     
      try {
     
        uniqueUsername(this.username)
        console.log('用户名可用')
      }catch (e) {
     
        console.log(e.message)
      }
    }, {
     
      delay: 2000
    })
  }

  @observable count = 0;
  @observable users = [];
  @observable username = '';

  // @action increment = () => {
     
  //   this.count = this.count + 1;
  // }

  // @action decrement = () => {
     
  //   this.count = this.count - 1;
  // }

  @action.bound increment () {
     
    this.count = this.count + 1;
  }

  @action.bound decrement () {
     
    this.count = this.count - 1;
  }

  // @action.bound async getData () {
     
  //   let { data } = await axios.get('https://api.github.com/users');
  //   runInAction(() => this.users = data);
  // }

  // getData = flow(function* () {
     
  //   let { data } = yield axios.get('https://api.github.com/users');
  //   this.users = data
  // }).bind(this)

  @computed get getResult () {
     
    return this.count * 10
  }

  @action.bound changeUsername (username) {
     
    this.username = username;
  }
}

const counter = new CounterStore();

function uniqueUsername (username) {
     
  return new Promise((resolve, reject) => {
     
    if (username === 'admin') {
     
      reject('用户名已经存在')
    }else {
     
      resolve()
    }
  })
}

export default counter;

App.js

import React, {
      Component } from "react";
import {
      inject, observer } from "mobx-react";

@inject("counter")
@observer
class App extends Component {
     
  componentDidMount() {
     
    // const { getData } = this.props.counter;
    // getData();
  }
  render() {
     
    const {
      counter } = this.props;
    return (
      <div>
        <button onClick={
     counter.increment}>+</button>
        <span>{
     counter.count}</span>
        <button onClick={
     counter.decrement}>-</button>
        <span>{
     counter.getResult}</span>
        <div>
          <input type="text" value={
     counter.username} onChange={
     e => counter.changeUsername(e.target.value)}/>
        </div>
        {
     /* 
{counter.users.map((user) => (
{user.id} {user.login}
))}
*/
} </div> ); } } export default App;

Mobx demo

Todo案例构建项目组件

实现一个对任务的增删改查任务和筛选等

src下的store放mobx代码,component放任务相关代码

App.js

import React, {
      Component } from 'react';
import AddTodo from './addTodo';
import TodoList from './todoList';
import TodoExtra from './todoExtra';

class App extends Component {
     
  render() {
     
    return <section className="todoapp">
      <AddTodo />
      <TodoList />
      <TodoExtra />
    </section>
  }
}

export default App;

addTodo.js

import React, {
      Component } from "react";
import {
      inject, observer } from "mobx-react";

@inject('todo')
@observer
class AddTodo extends Component {
     
  // 添加任务
  addTodo (event) {
     
    const {
      todoAdd } = this.props.todo;
    // 判断用户敲击的是否是回车键
    if (event.key === 'Enter') {
     
      // 获取用户在文本框中输入的内容
      const taskName = event.target.value;
      // 判断用户在文本框中是否输入了内容
      if (taskName.trim().length === 0) {
     
        // 阻止程序向下执行
        return;
      }
      // 将任务添加到任务列表数组中
      todoAdd (taskName);
      // 清空文本框中的内容
      event.target.value = '';
    }

  }
  render() {
     
    return (
      <header className="header">
        <h1>todos</h1>
        <input onKeyUp={
     this.addTodo.bind(this)} className="new-todo" placeholder="What needs to be done?" />
      </header>
    );
  }
}

export default AddTodo;

todoExtra.js

import React, {
      Component } from "react";
import {
      inject, observer } from "mobx-react";

@inject("todo")
@observer
class TodoExtra extends Component {
     
  render() {
     
    const {
      unfinishedTodoCount, changeFilter, filter } = this.props.todo;
    return (
      <footer className="footer">
        <span className="todo-count">
          <strong>{
     unfinishedTodoCount}</strong> item left
        </span>
        <ul className="filters">
          <li>
            <button className={
     filter === 'All' ? 'selected': ''} onClick={
     () => changeFilter('All')}>All</button>
          </li>
          <li>
            <button className={
     filter === 'Active' ? 'selected': ''} onClick={
     () => changeFilter('Active')}>Active</button>
          </li>
          <li>
            <button className={
     filter === 'Completed' ? 'selected': ''} onClick={
     () => changeFilter('Completed')}>Completed</button>
          </li>
        </ul>

        <button className="clear-completed">Clear completed</button>
      </footer>
    );
  }
}

export default TodoExtra;

todoList.js

import React, {
      Component } from "react";
import {
      inject, observer } from "mobx-react";

@inject("todo")
@observer
class TodoList extends Component {
     
  render() {
     
    const {
      todoDelete, changeCompleted, filterTodo } = this.props.todo;
    return (
      <section className="main">
        <input className="toggle-all" type="checkbox" />
        <ul className="todo-list">
          {
     filterTodo.map((todo, index) => {
     
            return (
              <li className={
     todo.isCompleted ? 'completed': ''} key={
     todo.taskName}>
                <div className="view">
                  <input onChange={
     event => changeCompleted(index, event.target.checked)} checked={
     todo.isCompleted} className="toggle" type="checkbox" />
                  <label>{
     todo.taskName}</label>
                  <button onClick={
     () => todoDelete(index)} className="destroy"></button>
                </div>
                <input className="edit" />
              </li>
            );
          })}
        </ul>
      </section>
    );
  }
}

export default TodoList;

TodoStore.js

import {
      observable, action, computed } from 'mobx';

class TodoStore {
     
  // todo 列表
  @observable todos = [];
  // 筛选条件
  @observable filter = 'All';
  // 添加任务
  @action.bound todoAdd (taskName) {
     
    this.todos.push({
     
      taskName,
      isCompleted: false
    })
  }
  // 删除任务
  @action.bound todoDelete (index) {
     
    this.todos.splice(index, 1);
  }
  // 更改任务的是否已完成状态
  @action.bound changeCompleted (index, flag) {
     
    this.todos[index].isCompleted = flag;
  }

  @computed get unfinishedTodoCount () {
     
    return this.todos.filter(todo => todo.isCompleted === false).length;
  }

  // 更改筛选条件
  @action.bound changeFilter (condition) {
     
    this.filter = condition;
  }

  // 监测筛选条件的变化 根据筛选条件对任务进行筛选
  @computed get filterTodo () {
     
    switch (this.filter) {
     
      case 'All':
        return this.todos;
      case 'Active':
        return this.todos.filter(todo => todo.isCompleted === false);
      case 'Completed':
        return this.todos.filter(todo => todo.isCompleted === true);
    }
  }
}

const todo = new TodoStore();

export default todo;

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App';
import todo from './stores/TodoStore';
import {
      Provider } from 'mobx-react';
import './index.css';

ReactDOM.render(
  <Provider todo={
     todo}><App /></Provider>,
  document.getElementById('root')
);

你可能感兴趣的:(react,react源码解析,reactjs,mobx)