redux 是一个状态管理库,独立存在,可以借助 react-redux 库与 react 配合使用。
介绍几个概念:
- store 包含状态数据,所有组件可以访问并操作里面的状态数据
- action 描述了发生了什么动作和事情,不包含状态本身
- reducer 纯函数,接受当前状态和一个动作作为参数,并返回一个新的状态
- dispatch 用于把动作(action)发送给 仓库(store),触发状态的变化
- subscribe 订阅状态变化,以便在状态发生改变的时候执行特定的逻辑,触发 UI 发生变化
下面是一个使用 redux 实现 counter 的简单示例
<html>
<button id="decrement">-</button>
<span id="count">0</span>
<button id="increment">+</button>
<script src="https://unpkg.com/redux@latest/dist/redux.min.js"></script>
<!--
state 一个对象,存放着我们管理的数据状态
action 一个对象,用来描述你想怎么修改数据
reducer 一个函数,更具 action 的描述生成一个新的 state
-->
<script>
// 1. 定义 reducer 函数:作用是根据不同的 action 对象,返回不同的新的 state
// state: 管理的数据初始状态
// action: 对象 type 标记当前想要做什么样的修改
function reducer(state = { count: 0 }, action) {
// 数据不可变:基于原始状态生成一个新的状态
if(action.type === 'INCREMENT') {
return { count: state.count + 1}
}
if(action.type === 'DECREMENT') {
return { count: state.count - 1}
}
return state
}
// 2. 使用 reducer 函数创建 store 实例
const store = Redux.createStore(reducer)
// 3. 通过 store 实例的 subscribe 订阅数据变化
// 回调函数可以在每次 state 发生变化的时候自动执行
store.subscribe(() => {
console.log('state 发生变化了', store.getState())
// 5. 通过 store 实例的 getState 方法获取最新状态更新到视图中
document.getElementById('count').innerText = store.getState().count
})
// 4. 通过 store 实例的 dispatch 函数触发 action 更改状态
const inBtn = document.getElementById('increment')
inBtn.addEventListener('click', () => {
store.dispatch({ type: 'INCREMENT' })
})
const dBtn = document.getElementById('decrement')
dBtn.addEventListener('click', () => {
store.dispatch({ type: 'DECREMENT' })
})
</script>
</html>
单独使用 redux 很复杂,官方推荐使用 @reduxjs/toolkit 简化 redux 的使用
- createSlice 一个用于创建 Redux Reducer 和 Action Creator 的函数,自动生成 Reducer 函数和相应的动作创建函数,大大减少了手动编写 Reducer 和动作创建函数的工作量。
- configureStore 封装了创建 Redux 仓库的过程
使用 react-redux 将 redux 注入到 react 中
-
Provider 将 store 注入到 react 中,一般包裹在顶层,以便内部的组件都能访问
-
useSelector 用于从 redux 中取对应的数据
store 目录
➜ store git:(main) tree .
.
├── index.js
└── modules
├── channelStore.js
└── counterStore.js
counterStore.js
import { createSlice } from '@reduxjs/toolkit'
const counterStore = createSlice({
name: 'counter',
initialState: {
count: 0
},
reducers: {
increment: (state) => {
state.count += 1
},
decrement: (state) => {
state.count -= 1
},
addToNum: (state, action) => {
state.count = action.payload
}
}
})
// 按需导出 actionCreator
export const { increment, decrement, addToNum } = counterStore.actions
export default counterStore.reducer
store/index.js 对各个模块的 store 做整合
import { configureStore } from "@reduxjs/toolkit"
import counterReducer from './modules/counterStore'
import channelStore from "./modules/channelStore"
const store = configureStore({
reducer: {
counter: counterReducer,
channel: channelStore
}
})
export default store
顶层组件将 redux 注入到 react 程序
<Provider store={store}>
<App />
</Provider>
使用 useSelector 取数据
const { count } = useSelector(state => state.counter)
使用 dispatch 触发 action
<button onClick={() => dispatch(decrement())}>-</button>
完整代码可以参考项目 https://github.com/zjy4fun/notes/tree/main/frontend/react/redux/react-redux-pro