一、Redux介绍

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

image

左边为用Redux组件之前,组件之间的通信是非常混乱的。用了Redux之后,多了一个Store,它独立于整个组件树,任何一个组件都可以使用Store中的数据,并且任何一个组件也可以通过固定的方式来修改Store中的数据。一旦Store里面的数据发生变化,所有用到Store的这个数据都会得到更新。

为什么要使用Redux?

  1. 独立于组件,无视组件之间的层级关系,简化通信问题

  2. 单项数据流清晰,易于定位bug

  3. 调试工具配套良好,方便调试

在Redux中修改数据的唯一方式是通过dispatch方法提交action对象

二、Redux快速体验

1、实现计数器

需求:不和任何框架绑定,不使用任何构建工具,使用纯Redux实现计数器

image

使用步骤:

  1. 定义一个 reducer 函数 (根据当前想要做的修改返回一个新的状态)

  2. 使用createStore方法传入 reducer函数 生成一个store实例对象

  3. 使用store实例的 subscribe方法 订阅数据的变化(数据一旦变化,可以得到通知)

  4. 使用store实例的 dispatch方法提交action对象 触发数据变化(告诉reducer你想怎么改数据)

  5. 使用store实例的 getState方法 获取最新的状态数据更新到视图中

<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>

<script>
  // 1、定义reducer函数 
  // 内部主要的工作是根据不同的action对象 返回不同的state
  function counterReducer (state = { count: 0 }, action) { // 参数一为state,为管理的数据的初始状态,参数二为action对象,其type属性标记当前想要做什么样的修改
    switch (action.type) { //根据不同的action对象做不同的输出
      case 'INCREMENT':
        return { count: state.count + 1 } // 注意:要返回一个新的对象,而不是直接加1或减1,因为数据不可变,必须基于一个原始的状态生成一个新的状态
      case 'DECREMENT':
        return { count: state.count - 1 }
      default:
        return state // 把原始的状态返回
    }
  }
  // 2、使用reducer函数生成store实例
  const store = Redux.createStore(counterReducer)

  // 3、订阅数据变化
  store.subscribe(() => { //回调函数在每次数据发生变化的时候自动执行
    console.log("state变化了")
  console.log(store.getState())
  // 5、通过store实例的getState方法获取最新的状态数据更新到视图中 document.getElementById(
'count').innerText = store.getState().count }) // 4、使用dispatch方法提交action对象,触发数据的变化---- 增 const inBtn = document.getElementById('increment') inBtn.addEventListener('click', () => { store.dispatch({ type: 'INCREMENT' }) }) // 4、使用dispatch方法提交action对象,触发数据的变化-----减 const dBtn = document.getElementById('decrement') dBtn.addEventListener('click', () => { store.dispatch({ type: 'DECREMENT' }) }) </script>

Redux数据流架构

Redux的难点是理解它对于数据修改的规则, 下图动态展示了在整个数据的修改中,数据的流向

image

视图部分(两个按钮)提交了一个action对象,点击按钮就会使用dispatch提交一个action对象。reducer函数根据不同的action对象做不同的输出,由于订阅了数据变化,回调函数在每次数据发生变化的时候自动执行。通过store实例的getState方法获取最新的状态数据更新到视图中。

为了职责清晰,Redux代码被分为三个核心的概念,我们学redux,其实就是学这三个核心概念之间的配合,三个概念分别是:

  1. state: 一个对象 存放着我们管理的数据

  2. action: 一个对象 用来描述你想怎么改数据

  3. reducer: 一个函数 根据action的描述更新state

三、Redux与React - 环境准备

Redux虽然是一个框架无关可以独立运行的插件,但是社区通常还是把它与React绑定在一起使用,以一个计数器案例体验一下Redux + React 的基础使用

1、配套工具

在React中使用redux,官方要求安装俩个其他插件 - Redux Toolkit 和 react-redux

(1)、Redux Toolkit(RTK)- 官方推荐编写Redux逻辑的方式,是一套工具的集合集,简化书写方式

image

(2)、react-redux - 用来 链接 Redux 和 React组件 的中间件

image

可以通过react-redux插件获取状态,更新状态也可以通过react-redux进行更新

2、配置基础环境

(1)、使用 CRA 快速创建 React 项目

npx create-react-app react-redux 

(2)、安装配套工具

cd react-redux
npm i @reduxjs/toolkit react-redux

(3)、启动项目

npm run start 

3、store目录结构设计

image

  1. 通常集中状态管理的部分都会单独创建一个单独的 store 目录

  2. 应用通常会有很多个子store模块,所以创建一个 modules 目录,在内部编写业务分类的子store

  3. store中的入口文件 index.js 的作用是组合modules中所有的子模块,并导出store

Redux与React - 实现counter

整体路径熟悉

image

1、使用React Toolkit 创建 counterStore

import { createSlice } from '@reduxjs/toolkit'
// 创建切片
const counterStore = createSlice({
  // createSlice 接收一个配置对象,包含三部分:name、initialState、reducers
  // 模块名称(独一无二)
  name: 'counter', // 用于生成 action 的 type,如 counter/increment
  // 初始数据
  initialState: {
    count: 1
  },
  // 修改数据的同步方法
  reducers: {
    increment (state) {
      state.count++
    },
    decrement(state){
      state.count--
    }
  }
})
// 结构出actionCreater
// 由于 reducers 中定义了 increment 和 decrement,RTK 自动为它们生成了同名的 action creator 函数。
// 组件中就可以通过 dispatch(increment()) 来触发状态更新。
const { increment,decrement } = counterStore.actions // 从actions属性中导出两个方法

// 获取reducer函数
// counterStore.reducer 是合并了所有 reducers 后的总 reducer 函数,需要交给 store 使用
const counterReducer = counterStore.reducer

// 导出
export { increment, decrement } // 具名导出:给组件 dispatch 用
export default counterReducer //  默认导出:给 store 注册用

 store/index.js

import { configureStore } from '@reduxjs/toolkit'

import counterReducer from './modules/counterStore'

export default configureStore({
  reducer: {
    // 注册子模块
    counter: counterReducer
  }
})

2、为React注入store

在src/index.js中导入store

react-redux负责把Redux和React 链接 起来,内置 Provider组件 通过 store 参数把创建好的store实例注入到应用中,链接正式建立

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

// 导入store
import store from './store'
// 导入store提供组件Provider
import { Provider } from 'react-redux'

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

3、React组件使用store中的数据

在React组件中使用store中的数据,需要用到一个钩子函数 - useSelector,它的作用是把store中的数据映射到组件中,使用样例如下

image

App.js中的代码如下:

import './App.css';
import {useSelector} from 'react-redux'
function App() {
  const {count} = useSelector(state => state.counter) // 解构出状态count
  return (
    <div className="App">
      this is app{count}
    </div>
  );
}

export default App;  

4、React组件修改store中的数据

React组件中修改store中的数据需要借助另外一个hook函数 - useDispatch,它的作用是生成提交action对象的dispatch函数,使用样例如下:

App.js中的代码如下:

import './App.css';
import {useSelector,useDispatch} from 'react-redux'
// 导入创建action对象的方法
import {decrement,increment} from './store/modules/counterStore'
function App() {
  const {count} = useSelector(state => state.counter) // 解构出状态count
  // 得到dispatch函数
  const dispatch = useDispatch()
  return (
    <div className="App">
      {/** 调用dispatch提交action对象 */}
      <button onClick={()=>dispatch(decrement())}>-</button>
      <span>{count}</span>
      <button onClick={()=>dispatch(increment())}>+</button>
    </div>
  );
}

export default App;

组件中使用userSelect这个hook函数获取store中的数据

组件中使用useDispatch这个hook函数获取dispatch方法

效果:

image

Redux与React - 提交action传参

需求:组件中有俩个按钮 add to 10 和 add to 20 可以直接把count值修改到对应的数字,目标count值是在组件中传递过去的,需要在提交action的时候传递参数

image

实现方式:在reducers的同步修改方法中添加action对象参数,在调用actionCreater的时候传递参数,参数会被传递到action对象payload属性上

image

counterStore.js修改如下:

import { createSlice } from '@reduxjs/toolkit'
// 创建切片
const counterStore = createSlice({
  // createSlice 接收一个配置对象,包含三部分:name、initialState、reducers
  // 模块名称(独一无二)
  name: 'counter', // 用于生成 action 的 type,如 counter/increment
  // 初始数据
  initialState: {
    count: 1
  },
  // 修改数据的同步方法
  reducers: {
    increment (state) {
      state.count++
    },
    decrement(state){
      state.count--
    },
    addToNum(state,action){
        state.count = action.payload
    }
  }
})
// 结构出actionCreater
// 由于 reducers 中定义了 increment 和 decrement,RTK 自动为它们生成了同名的 action creator 函数。
// 组件中就可以通过 dispatch(increment()) 来触发状态更新。
const { increment,decrement,addToNum } = counterStore.actions // 从actions属性中导出两个方法

// 获取reducer函数
// counterStore.reducer 是合并了所有 reducers 后的总 reducer 函数,需要交给 store 使用
const counterReducer = counterStore.reducer

// 导出
export { increment, decrement,addToNum } // 具名导出:给组件 dispatch 用
export default counterReducer //  默认导出:给 store 注册用

App.js中内容如下:

import './App.css';
import {useSelector,useDispatch} from 'react-redux'
// 导入创建action对象的方法
import {decrement,increment,addToNum} from './store/modules/counterStore'
function App() {
  const {count} = useSelector(state => state.counter) // 解构出状态count
  // 得到dispatch函数
  const dispatch = useDispatch()
  return (
    <div className="App">
      {/** 调用dispatch提交action对象 */}
      <button onClick={()=>dispatch(decrement())}>-</button>
      <span>{count}</span>
      <button onClick={()=>dispatch(increment())}>+</button>
      <button onClick={()=>dispatch(addToNum(10))}>add to 10</button>
      <button onClick={()=>dispatch(addToNum(20))}>add to 20</button>
    </div>
  );
}

export default App;

效果:

image

四、Redux与React - 异步action处理

需求理解

image

实现步骤

(1)、创建store的写法保持不变,配置好同步修改状态的方法

(2)、单独封装一个函数,在函数内部return一个新函数,在新函数中

  2.1 封装异步请求获取数据

  2.2 调用同步actionCreater传入异步数据生成一个action对象,并使用dispatch提交

(3)、组件中dispatch的写法保持不变

安装axios

npm install axios

channelStore.js中的内容如下:

import { createSlice } from '@reduxjs/toolkit'
import axios from 'axios'
// 创建切片
const channelStore = createSlice({
  // createSlice 接收一个配置对象,包含三部分:name、initialState、reducers
  // 模块名称(独一无二)
  name: 'channel', // 用于生成 action 的 type
  // 初始数据
  initialState: {
    channelList: []
  },
  // 修改数据的同步方法
  reducers: {
    setChannelList (state, action) {
      state.channelList = action.payload
    }
  }
})
// 结构出actionCreater
const { setChannelList } = channelStore.actions // 从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))
  }
}
// 获取reducer函数
// counterStore.reducer 是合并了所有 reducers 后的总 reducer 函数,需要交给 store 使用
const channelReducer = channelStore.reducer

// 导出
export { fetchChannelList } // 具名导出:给组件 dispatch 用
export default channelReducer //  默认导出:给 store 注册用

store/index.js中添加如下内容

import { configureStore } from '@reduxjs/toolkit'

import counterReducer from './modules/counterStore'
import channelReducer from './modules/channelStore'

export default configureStore({
  reducer: {
    // 注册子模块
    counter: counterReducer,
    channel: channelReducer
  }
})

App.js

import './App.css';
import {useSelector,useDispatch} from 'react-redux'
import { useEffect } from 'react'
// 导入创建action对象的方法
import {fetchChannelList} from './store/modules/channelStore'
function App() {
  const { channelList } = useSelector(state => state.channel)
  const dispatch = useDispatch()
  useEffect(() => {
    dispatch(fetchChannelList())
  }, [dispatch])
  return (
    <ul>
      {channelList.map(item => <li key={item.id}>{item.name}</li>)}
    </ul>
  );
}

export default App;

效果如下:

image

 

五、Redux调试 - devtools

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

image

 

posted on 2026-09-17 11:19  周文豪  阅读(5)  评论(0)    收藏  举报