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",
}
}
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)
}
}
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)
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
想要触发app
的action
修改,按照如下办法
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()
}
}
modules/user/index.ts
中constructor
接收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修改了')
}
}
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
要想修改state
需要使用runInAction
函数
await
之后的任何操作都不与其同在一个 tick
中,因此它们需要使用 action
包装。 在这里,我们可以利用 runInAction
:
主要的就是在runInAction
函数中调用修改state
的action
代码部分
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>