React使用MobX作为全局状态管理

React使用MobX作为全局状态管理

这里以模块化方式进行使用
MobX官网

安装依赖

yarn add mobx mobx-react-lite 安装mobx-react-lite是因为目前项目上使用的都是函数式组件
目前项目上的依赖如下

{
	"dependencies": {
	    "antd": "^4.16.13",
	    "antd-img-crop": "^3.16.0",
	    "axios": "^0.20.0",
	    "crypto-js": "^4.0.0",
	    "echarts": "^5.1.1",
	    "mobx": "^6.6.1",
	    "mobx-react-lite": "^3.4.0",
	    "react": "^17.0.1",
	    "react-dom": "^17.0.1",
	    "react-router-dom": "^5.2.0",
	    "react-scripts": "^4.0.3",
	    "react-transition-group": "^4.4.1",
	  }
}

使用方式

  1. makeAutoObservable, 让store中声明的类变成响应式官方地址

    import { makeAutoObservable } from 'mobx'
    export default class TodoListStore {
      list = [
        {
          key: 1,
          title: 'Vue'
        },
        {
          key: 2,
          title: 'React'
        },
        {
          key: 3,
          title: 'Angular'
        }
      ]
      constructor() {
        makeAutoObservable(this)
      }
      /** 新增 */
      insertTodoList(item) {
        this.list.push(item)
      }
    }
    
  2. observer注解可以作为一个函数进行调用,从而一次性将整个对象变成可观察的官方地址 , 这里没有使用antd中的list稍后说明

    import React, { useRef } from 'react'
    import { Button, Input } from 'antd'
    import { observer } from 'mobx-react-lite'
    import TodoListStore  from '@/store'
    const TodoList = () => {
      const inputValueRef = useRef<Input>(null)
      const todoListStore = new TodoListStore()
      /** 新增按钮 */
      const insertTodo = () => {
        if (inputValueRef.current) {
          todoListStore.insertTodoList({
            key: new Date().getTime(),
            title: inputValueRef.current.state.value
          })
        }
      }
      return (
        <div>
          <div>
            <Input
              placeholder="输入些什么"
              ref={inputValueRef}
              style={{ width: '200px', marginRight: '10px' }}
            />
            <Button type="primary" onClick={insertTodo}>
              新增
            </Button>
          </div>
          {
            todoListStore.list.map(item => {
              return <span key={item.key}>{item.title}---</span>
            })
          }
    	   </div>
      )
    }
    export default observer(TodoList)
    
  3. 效果图
    在这里插入图片描述

模块化的方式使用

文件目录使用类似于vuex的方式进行命名
在这里插入图片描述

modules/app/index.ts

import { makeAutoObservable} from 'mobx'
export default class AppStore {
  /** 侧边栏的状态 false 关闭 true 展开 */
  app = 'app'
  constructor() {
    makeAutoObservable(this)
  }
  changeApp(text: string) {
    this.app = text
  }
}

modules/todoList/index.ts

import { makeAutoObservable} from 'mobx'
export default class TodoListStore {
  /** 侧边栏的状态 false 关闭 true 展开 */
  todoList = 'todoList '
  constructor() {
    makeAutoObservable(this)
  }
  changeTodoList (text: string) {
    this.todoList = text
  }
}

modules/user/index.ts

import { makeAutoObservable} from 'mobx'
export default class UserStore {
  /** 侧边栏的状态 false 关闭 true 展开 */
  user = 'user'
  constructor() {
    makeAutoObservable(this)
  }
  changeUser(text: string) {
    this.user = text
  }
}

store/index.ts

import { createContext, useContext } from 'react'
import AppStore from './modules/app'
import UserStore from './modules/user'
import TodoListStore from './modules/todoList'

class RootState {
  appStore: AppStore
  userStore: UserStore
  todoListStore: TodoListStore
  constructor() {
    this.appStore = new AppStore()
    this.userStore = new UserStore()
    this.todoListStore = new TodoListStore()
  }
}

const rootStore = new RootState()
const context = createContext(rootStore)
const useStore = () => useContext(context)
export { useStore }

使用方式

import React from 'react'
import { observer } from 'mobx-react-lite'
import { useStore } from '@/store'

const TodoList = () => {
  const { appStore, todoListStore, userStore } = useStore()
  const buttonChange = ()=> {
    appStore.changeApp('app 被修改了')
    todoListStore.changeTodoList('todoList 被修改了')
    userStore.changeUser('user 被修改了')
  }
  return (
    <div>
      {appStore.app} --- {userStore.user}-----{todoListStore.todoList}
      <button onClick={buttonChange}>修改</button>
    </div>
  )
}
export default observer(TodoList)

在这里插入图片描述

注意重点来了, 如果模块化的store之间想要相互调用可以使用如下办法

解释一下, 如果我的store下面, user模块中的action想要触发appaction修改,按照如下办法

  1. store/index.ts下面初始化的时候, 将this.appStore 传入
    class RootState {
      appStore: AppStore
      userStore: UserStore
      todoListStore: TodoListStore
      constructor() {
        this.appStore = new AppStore()
        this.userStore = new UserStore(this.appStore)
        this.todoListStore = new TodoListStore()
      }
    }
    
  2. modules/user/index.tsconstructor接收appStore
    import { makeAutoObservable} from 'mobx'
    import AppStore from '../app'
    export default class UserStore {
      /** 侧边栏的状态 false 关闭 true 展开 */
      user = 'user'
      appStore: AppStore
      constructor(appStore: AppStore) {
        makeAutoObservable(this)
        this.appStore = appStore
      }
      changeUser(text: string) {
        this.user = text
        this.appStore.changeApp('app 被 user中的action修改了')
      }
    }
    
  3. 使用方式
    import React from 'react'
    import { observer } from 'mobx-react-lite'
    import { useStore } from '@/store'
    
    const TodoList = () => {
      const { appStore, todoListStore, userStore } = useStore()
      const buttonChange = ()=> {
        todoListStore.changeTodoList('todoList 被修改了')
        userStore.changeUser('user 被修改了')
      }
      return (
        <div>
          {appStore.app} --- {userStore.user}-----{todoListStore.todoList}
          <button onClick={buttonChange}>修改</button>
        </div>
      )
    }
    export default observer(TodoList)
    
    在这里插入图片描述

发送请求异步action

异步的action要想修改state 需要使用runInAction函数

await 之后的任何操作都不与其同在一个 tick 中,因此它们需要使用 action 包装。 在这里,我们可以利用 runInAction

主要的就是在runInAction函数中调用修改stateaction

代码部分

import { makeAutoObservable, runInAction } from 'mobx'
export default class UserStore {
  loading = false
  constructor() {
    makeAutoObservable(this)
  }
  changeLoading(flag: boolean) {
    this.loading = flag
  }
  async testUserFetch() {
    try {
      this.changeLoading(true)
      const response = await fetch('https://api.thecatapi.com/v1/images/search?limit=1')
      const data = await response.json()
      console.log(data, 'data')
      runInAction(()=> {
        this.changeLoading(false)
      })
    } catch (error) {
      console.log(error)
    }
  }
}

调用使用

import React from 'react'
import { observer } from 'mobx-react-lite'
import { useStore } from '@/store'

const TodoList = () => {
  const { userStore } = useStore()
  const buttonChange = () => {
    userStore.testUserFetch()
  }
  return (
    <div>
      { userStore.loading ? 'loading.....' : '请求还没开始或者结束啦' }
      <button onClick={buttonChange}>发送请求</button>
    </div>
  )
}
export default observer(TodoList)

效果图
在这里插入图片描述

意想不到的问题

在编写todoList代码中, 为啥没有使用antd中的List组件呢, 如果按照正常的方式修改state的数据,并且使用List组件的效果,页面是无法实时更新的, 但是使用正常的map遍历普通的html是可以更新

下列代码无法正常更新

import { makeAutoObservable } from 'mobx'
export default class TodoListStore {
  list = [
    {
      key: 1,
      title: 'Vue'
    },
    {
      key: 2,
      title: 'React'
    },
    {
      key: 3,
      title: 'Angular'
    }
  ]
  constructor() {
    makeAutoObservable(this)
  }
  /** 新增 */
  insertTodoList(item) {
    this.list.push(item)
  }
}

<List
  style={{ flex: 1 }}
  header={<div>ToDo List</div>}
  bordered
  dataSource={todoListStore.list}
  renderItem={(item: any) => (
    <List.Item className="todo-item" key={item.key}>
      {item.title}
    </List.Item>
  )}
></List>

需要进行如下改造

store中添加computed

/** 返回 antd-list 的数据, 使用 computed 的方式处理, 并且还要调用 slice 方法, 才可以让 antd-list 实时变化 */
get dataSourceTodoList() {
  return this.list.slice()
}
import { makeAutoObservable } from 'mobx'
export default class TodoListStore {
  list = [
    {
      key: 1,
      title: 'Vue'
    },
    {
      key: 2,
      title: 'React'
    },
    {
      key: 3,
      title: 'Angular'
    }
  ]
  todoList = 'todoList'
  changeTodoList (text: string) {
    this.todoList = text
  }
  constructor() {
    makeAutoObservable(this)
  }
  /** 返回 antd-list 的数据, 使用 computed 的方式处理, 并且还要调用 slice 方法, 才可以让 antd-list 实时变化 */
  get dataSourceTodoList() {
    return this.list.slice()
  }
  /** 新增 */
  insertTodoList(item) {
    this.list.push(item)
  }
}

dataSource修改为dataSourceTodoList

<List
  style={{ flex: 1 }}
  header={<div>ToDo List</div>}
  bordered
  dataSource={todoListStore.dataSourceTodoList}
  renderItem={(item: any) => (
    <List.Item className="todo-item" key={item.key}>
      {item.title}
    </List.Item>
  )}
></List>
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值