Redux 介绍

Redux 是 React 最常用的集中状态管理工具,类似于 Vue 中的 Pinia(Vuex),可以独立于框架运行

作用:通过集中管理的方式管理应用的状态

为什么要使用Redux?

  1. 独立于组件,无视组件之间的层级关系,简化通信问题
  2. 单项数据流清晰,易于定位bug
  3. 调试工具配套良好,方便调试

使用步骤

  1. 定义一个 reducer 函数 (根据当前想要做的修改返回一个新的状态)
  2. 使用 createStore 方法传入 reducer 函数 生成一个 store 实例对象
  3. 使用 store 实例的 subscribe 方法 订阅数据的变化(数据一旦变化,可以得到通知)
  4. 使用 store 实例的 dispatch 方法提交 action 对象 触发数据变化(告诉 reducer 你想怎么改数据)
  5. 使用 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
}
// 通过createStore生成一个store实例对象
const store = Redux.createStore(counterReducer)
// 订阅数据的变化,并做出反应,getState()获取最新的状态数据
store.subscribe(() => {
console.log('变化了', store.getState())
document.getElementById('count').innerText = store.getState().count
})
// dispatch方法用于提交action对象,触发数据变化
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数据流架构

  1. state: 一个对象 存放着我们管理的数据
  2. action: 一个对象 用来描述你想怎么改数据
  3. reducer: 一个函数 根据action的描述更新state

准备环境

  1. Redux Toolkit(RTK):官方推荐编写Redux逻辑的方式,是一套工具的集合集,简化书写方式
  2. react-redux:用来 链接 Redux 和 React组件 的中间件
1
npm i @reduxjs/toolkit  react-redux 

在 React 实现 counter

使用 React Toolkit 创建 counterStore

image.png

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
// counter.js
import { createSlice } from "@reduxjs/toolkit";
// 创建store
const counterStore = createSlice({
// 模块名
name: 'counter',
// 初始化状态数据
  initialState: {
    count: 1
  },
  // 修改数据的同步方法,可以直接修改
  reducers:{
    increment(state) {
      state.count++
    },
    decrement(state){
      state.count--
    }
  }
})
// 解构出actionCreater
const { increment, decrement } = counterStore.actions
// 获取reducer函数
const counterReducer = counterStore.reducer
// 导出
export { increment, decrement }
export default counterReducer
1
2
3
4
5
6
7
8
9
10
11
12
13
// index.js
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
// index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import store from './store' // 导入store
import { Provider } from 'react-redux'; // 导入store提供的组件Provider

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
// 提供store数据
  <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
// App.js
import { useSelector } from "react-redux"; // 导入useSelector

function App () {
// 解构出state目标
  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";
// 导入创建action对象的方法
import { decrement, increment } from './store/modules/counter'

function App () {
  const { count } = useSelector(state => state.counter)
  // 得到dispatch函数
  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) {
      // console.log(action.payload)
      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 处理

实现步骤

  1. 创建 store 的写法保持不变,配置好同步修改状态的方法
  2. 单独封装一个函数,在函数内部 return 一个新函数,在新函数中
    1. 封装异步操作
    2. 调用同步 actionCreater 传入异步数据生成一个 action 对象,并使用 dispatch 提交
  3. 组件中 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) {
      // console.log(action.payload)
      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调试 - devtools

Redux官方提供了针对于Redux的调试工具,支持实时state信息展示,action提交信息查看等

image.png

image.png