Redux 介绍
Redux 是 React 最常用的集中状态管理工具,类似于 Vue 中的 Pinia(Vuex),可以独立于框架运行
作用:通过集中管理的方式管理应用的状态
为什么要使用Redux?
- 独立于组件,无视组件之间的层级关系,简化通信问题
- 单项数据流清晰,易于定位bug
- 调试工具配套良好,方便调试
使用步骤:
- 定义一个 reducer 函数 (根据当前想要做的修改返回一个新的状态)
- 使用 createStore 方法传入 reducer 函数 生成一个 store 实例对象
- 使用 store 实例的 subscribe 方法 订阅数据的变化(数据一旦变化,可以得到通知)
- 使用 store 实例的 dispatch 方法提交 action 对象 触发数据变化(告诉 reducer 你想怎么改数据)
- 使用 store 实例的 getState 方法 获取最新的状态数据更新到视图中
实现一个计数器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| <!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <button id="deBtn">-</button> <span id="count">0</span> <button id="inBtn">+</button> <script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.1.0/redux.min.js"></script> <script> function counterReducer (state={count: 0}, action) { if(action.type === 'INCREMENT') return {count: state.count + 1} if(action.type === 'DECREMENT') return {count: state.count - 1} return state } const store = Redux.createStore(counterReducer) store.subscribe(() => { console.log('变化了', store.getState()) document.getElementById('count').innerText = store.getState().count }) const inBtn = document.getElementById('inBtn') inBtn.addEventListener('click', () => { store.dispatch({ type: 'INCREMENT' }) }) const deBtn = document.getElementById('deBtn') deBtn.addEventListener('click', () => { store.dispatch({ type: 'DECREMENT' }) }) </script> </body> </html>
|
Redux数据流架构:
- state: 一个对象 存放着我们管理的数据
- action: 一个对象 用来描述你想怎么改数据
- reducer: 一个函数 根据action的描述更新state
准备环境
- Redux Toolkit(RTK):官方推荐编写Redux逻辑的方式,是一套工具的集合集,简化书写方式
- react-redux:用来 链接 Redux 和 React组件 的中间件
1
| npm i @reduxjs/toolkit react-redux
|
在 React 实现 counter
使用 React Toolkit 创建 counterStore:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| import { createSlice } from "@reduxjs/toolkit";
const counterStore = createSlice({ name: 'counter', initialState: { count: 1 }, reducers:{ increment(state) { state.count++ }, decrement(state){ state.count-- } } })
const { increment, decrement } = counterStore.actions
const counterReducer = counterStore.reducer
export { increment, decrement } export default counterReducer
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| import { configureStore } from "@reduxjs/toolkit"; import counterReducer from "./modules/counter"; const store = configureStore({ reducer: { counter: counterReducer } }) export default store
|
为 React 注入 store:
react-redux 负责把 Redux 和 React 链接 起来,内置 Provider组件 通过 store 参数把创建好的 store 实例注入到应用中,链接正式建立
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; import store from './store' import { Provider } from 'react-redux'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <Provider store={store}> <App /> </Provider> );
|
React 组件使用 store 中的数据:
在 React 组件中使用 store 中的数据,需要用到一个钩子函数 useSelector
,它的作用是把 store 中的数据映射到组件中
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import { useSelector } from "react-redux"; function App () { const { count } = useSelector(state => state.counter) return ( <div> Hello{count} </div> ) } export default App;
|
React 组件中修改 store 数据:
React 组件中修改 store 中的数据需要借助另外一个 hook函数useDispatch
,它的作用是生成提交 action对象 的 dispatch 函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import { useSelector, useDispatch } from "react-redux";
import { decrement, increment } from './store/modules/counter' function App () { const { count } = useSelector(state => state.counter) const dispatch = useDispatch() return ( <div> {/* 调用dispatch函数提交action对象 */} <button onClick={() => dispatch(decrement())}>-</button> <span>{count}</span> <button onClick={() => dispatch(increment())}>+</button> </div> ) }
export default App;
|
提交 action 传参
在reducers的同步修改方法中添加action对象参数,在调用actionCreater的时候传递参数,参数会被传递到action对象payload属性上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import { createSlice } from "@reduxjs/toolkit"; const counterStore = createSlice({ name: 'counter', initialState: { count: 0 }, reducers:{ addCount(state, action) { state.count += action.payload } } }) const { increment, decrement, addCount } = counterStore.actions const counterReducer = counterStore.reducer export { increment, decrement, addCount } export default counterReducer
|
1
| <button onClick={() => dispatch(addCount(10))}>+10</button>
|
异步 action 处理
实现步骤
- 创建 store 的写法保持不变,配置好同步修改状态的方法
- 单独封装一个函数,在函数内部 return 一个新函数,在新函数中
- 封装异步操作
- 调用同步 actionCreater 传入异步数据生成一个 action 对象,并使用 dispatch 提交
- 组件中 dispatch 的写法保持不变
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| import { createSlice } from "@reduxjs/toolkit"; const counterStore = createSlice({ reducers:{ addCount(state, action) { state.count += action.payload } } }) const { increment, decrement, addCount } = counterStore.actions
const fetchCounter = (num) => { return async (dispatch) => { await setTimeout(() => { dispatch(addCount(num)) }, 3000) } } const counterReducer = counterStore.reducer
export { increment, decrement, addCount, fetchCounter }
export default counterReducer
|
Redux官方提供了针对于Redux的调试工具,支持实时state信息展示,action提交信息查看等