Mobx
介绍
一个集中状态管理工具。一个独立的响应式的库,和React一起使用。Mobx做响应式数据建模,React作为UI试图框架渲染内容。
需要使用中间件链接两者
npm create vite react-mobx --template react
npm install mobx mobx-react-lite
基础使用
- 定义store
- 定义数据状态state
- 在构造器中实现数据响应式处理 makeAutoObservble
- 定义修改数据的函数action
- 实例化store并导出
import { makeAutoObservable } from 'mobx'
class CounterStore {
count = 0 // 定义数据
constructor() {
makeAutoObservable(this) // 响应式处理
}
// 定义修改数据的方法
addCount = () => {
this.count++
}
}
const counter = new CounterStore()
export default counter
- React使用store
- 在组件中导入counterStore实例对象
- 在组件中使用counterStore实例对象中的数据
- 通过事件调用修改数据的方法修改store中的数据
- 让组件响应数据变化
// 导入counterStore
import counterStore from './store'
// 导入observer方法
import { observer } from 'mobx-react-lite'
function App() {
return (
<div className="App">
<button onClick={() => counterStore.addCount()}>
{counterStore.count}
</button>
</div>
)
}
// 包裹组件让视图响应数据变化
export default observer(App)
计算属性
- 声明一个存在的数据
- 通过get关键词 依赖已声明数据定义计算属性
- 在 makeAutoObservable 方法中标记计算属性
- 在使用时直接调用就行
import { computed, makeAutoObservable } from 'mobx'
class CounterStore {
list = [1, 2, 3, 4, 5, 6]
constructor() {
makeAutoObservable(this, {
filterList: computed
})
}
// 修改原数组
changeList = () => {
this.list.push(7, 8, 9)
}
// 定义计算属性
get filterList () {
return this.list.filter(item => item > 4)
}
}
const counter = new CounterStore()
export default counter
异步数据处理
- 在mobx中编写异步请求方法 获取数据 存入state中
- 组件中通过 useEffect + 空依赖 触发action函数的执行
// 异步的获取
import { makeAutoObservable } from 'mobx'
import axios from 'axios'
class ChannelStore {
channelList = []
constructor() {
makeAutoObservable(this)
}
// 只要调用这个方法 就可以从后端拿到数据并且存入channelList
setChannelList = async () => {
const res = await axios.get('http://geek.itheima.net/v1_0/channels')
this.channelList = res.data.data.channels
}
}
const channlStore = new ChannelStore()
export default channlStore
///App.jsx
import { useEffect } from 'react'
import { useStore } from './store'
import { observer } from 'mobx-react-lite'
function App() {
const { channlStore } = useStore()
// 1. 使用数据渲染组件
// 2. 触发action函数发送异步请求
useEffect(() => {
channlStore.setChannelList()
}, [])
return (
<ul>
{channlStore.channelList.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
)
}
// 让组件可以响应数据的变化[也就是数据一变组件重新渲染]
export default observer(App)
模块化
- 拆分模块js文件,每个模块中定义自己独立的state/action
- 在store/index.js中导入拆分之后的模块,进行模块组合
- 利用React的context的机制导出统一的useStore方法,给业务组件使用
组合模块导出,之后在组件中可引入useStore直接使用
import React from 'react'
import counter from './counterStore'
import task from './taskStore'
class RootStore {
constructor() {
this.counterStore = counter
this.taskStore = task
}
}
const rootStore = new RootStore()
// context机制的数据查找链 Provider如果找不到 就找createContext方法执行时传入的参数
const context = React.createContext(rootStore)
const useStore = () => React.useContext(context)
// useStore() => rootStore { counterStore, taskStore }
export { useStore }
Redux
介绍
为什么要使用Redux?
- 独立于组件,无视组件之间的层级关系,简化通信问题
- 单项数据流清晰,易于定位bug
- 调试工具配套良好,方便调试
# 创建项目
$ npm create vite react-redux --template react
# 安装redux配套工具
$ npm install @reduxjs/toolkit react-redux
基础使用
- 创建couterStore
- 使用toolkit的createSlice方法创建一个独立的子模块
- 使用configureStore语法组合子模块
子模块:
import { createSlice } from '@reduxjs/toolkit'
const counter = createSlice({
// 模块名称独一无二
name: 'counter',
// 初始数据
initialState: {
count: 1
},
// 修改数据的同步方法
reducers: {
add (state) {
state.count++
}
}
})
const { add } = counter.actions
const counterReducer = counter.reducer
// 导出修改数据的函数
export { add }
// 导出reducer
export default counterReducer
组合子模块:
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from './counterStore'
export default configureStore({
reducer: {
// 注册子模块
counter: counterReducer
}
})
- 为React提供Redux Store
在入口文件中,渲染根组件的位置通过Provider提供store数据
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
// 导入store
import store from './store'
// 导入store提供组件Provider
import { Provider } from 'react-redux'
ReactDOM.createRoot(document.getElementById('root')).render(
// 提供store数据
<Provider store={store}>
<App />
</Provider>
)
- 组件中使用store中的数据
组件使用store中的数据需要借助一个hook方法,叫做useSelector
useSelector(state => state.模块名) 方法的返回值为一个对象,对象中包含store子模块中的所有数据
import { useSelector } from 'react-redux'
function App () {
// 使用数据
const { count } = useSelector(state => state.counter)
return (
<div className="App">
{count}
<button onClick={clickHandler}>+</button>
</div>
)
}
export default App
- 组件修改store中的数据
- 使用counterStore模块中导出的add方法创建action对象
- 通过dispatch函数以action作为参数传入完成数据更新
import { useSelector, useDispatch } from 'react-redux'
import { add } from './store/counterStore'
function App () {
// 使用数据
const { count } = useSelector(state => state.counter)
// 修改数据
const dispatch = useDispatch()
const clickHandler = () => {
// 1. 生成action对象
const action = add()
// 2. 提交action进行数据更新
dispatch(action)
}
return (
<div className="App">
{count}
<button onClick={clickHandler}>+</button>
</div>
)
}
export default App
- 组件修改数据并传参
- 通过action的payload属性接收参数
import { createSlice } from "@reduxjs/toolkit"
const counterStore = createSlice({
name: 'counter', // 独一无二不重复的名字语义化
// 定义初始化的数据
initialState: {
taskList: ['react']
},
reducers: {
// action为一个对象 对象中有一个固定的属性叫做payload 为传递过来的参数
addTaskList (state, action) {
state.taskList.push(action.payload)
}
}
})
// 生成修改数据的方法导出
const { addTaskList } = counterStore.actions
export { addTaskList }
// 生成reducer 导出 供index.js做组合模块
const counterReducer = counterStore.reducer
export default counterReducer
- dispatch的时候传入实参
<button onClick={() => dispatch(addTaskList('vue'))}>addList</button>
异步处理
创建异步函数
// 创建异步
const { setChannelList } = channelStore.actions
const url = 'http://geek.itheima.net/v1_0/channels'
// 封装一个函数 在函数中return一个新函数 在新函数中封装异步
// 得到数据之后通过dispatch函数 触发修改
const fetchChannelList = () => {
return async (dispatch) => {
const res = await axios.get(url)
dispatch(setChannelList(res.data.data.channels))
}
}
export { fetchChannelList }
useEffect使用
// 使用数据
const { channelList } = useSelector(state => state.channel)
useEffect(() => {
dispatch(fetchChannelList())
}, [dispatch])