一、Redux介绍

左边为用Redux组件之前,组件之间的通信是非常混乱的。用了Redux之后,多了一个Store,它独立于整个组件树,任何一个组件都可以使用Store中的数据,并且任何一个组件也可以通过固定的方式来修改Store中的数据。一旦Store里面的数据发生变化,所有用到Store的这个数据都会得到更新。
为什么要使用Redux?
-
独立于组件,无视组件之间的层级关系,简化通信问题
-
单项数据流清晰,易于定位bug
-
调试工具配套良好,方便调试
在Redux中修改数据的唯一方式是通过dispatch方法提交action对象
二、
需求:不和任何框架绑定,不使用任何构建工具,使用纯Redux实现计数器

使用步骤:
-
定义一个 reducer 函数 (根据当前想要做的修改返回一个新的状态)
-
使用createStore方法传入 reducer函数 生成一个store实例对象
-
使用store实例的 subscribe方法 订阅数据的变化(数据一旦变化,可以得到通知)
-
使用store实例的 dispatch方法提交action对象 触发数据变化(告诉reducer你想怎么改数据)
-
使用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的难点是理解它对于数据修改的规则, 下图动态展示了在整个数据的修改中,数据的流向

视图部分(两个按钮)提交了一个action对象,点击按钮就会使用dispatch提交一个action对象。reducer函数根据不同的action对象做不同的输出,由于订阅了数据变化,回调函数在每次数据发生变化的时候自动执行。通过store实例的getState方法获取最新的状态数据更新到视图中。
为了职责清晰,Redux代码被分为三个核心的概念,我们学redux,其实就是学这三个核心概念之间的配合,三个概念分别是:
-
state: 一个对象 存放着我们管理的数据
-
action: 一个对象 用来描述你想怎么改数据
-
reducer: 一个函数 根据action的描述更新state
三、
Redux虽然是一个框架无关可以独立运行的插件,但是社区通常还是把它与React绑定在一起使用,以一个计数器案例体验一下Redux + React 的基础使用
1、
在React中使用redux,官方要求安装俩个其他插件 - Redux Toolkit 和 react-redux

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

可以通过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模块,所以创建一个
modules目录,在内部编写业务分类的子store -
store中的入口文件 index.js 的作用是组合modules中所有的子模块,并导出store

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
}
})
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();
在React组件中使用store中的数据,需要用到一个钩子函数 - useSelector,它的作用是把store中的数据映射到组件中,使用样例如下

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


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

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;
效果:

需求理解

实现步骤
(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;
效果如下:

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

浙公网安备 33010602011771号