React学习(四) --- Redux
文章目录
- 1. Redux前置知识
- 1.1 纯函数回顾
- 1.2 Redux的三个核心概念
- 2. Redux使用
- 2.1 规范化Redux结构
- 2.2 Redux的三大规则
- 2.3 react-redux --- 高阶组件封装库
- 2.4 异步获取数据
- 2.5 react代码调试工具
- 2.6 Redux模块拆分
- 3. RTK工具包
- 3.1 redux toolkit
- 3.2 使用RTK
- 3.2.1 创建reducer片段 ---createSlice
1. Redux前置知识
1.1 纯函数回顾
- 确定输入,一定会产生确定的输出;
- 函数在执行过程中,不能产生副作用;
判断下面三个函数是否是纯函数
答案:
- 是
- 不是 (依赖外界)
- 不是 (改变外界)
1.2 Redux的三个核心概念
-
store
-
action
-
reducer
❗️
reducer
是一个纯函数,主要人物是将state
和action
结合起来生成一个新的state
返回,这个返回值将替换之前的state
。
修改state
流程:
state.dispatch({type: ... , action: {...})
- 触发reducer,产生新
state
state
更新
如何知道store数据修改了呢?
答:使用subscribe
componentDidMount() {// 订阅store数据this.unsubscribe = store.subscribe(() => {console.log('store change:', store.getState());})}componentWillUnmount() {// 取消订阅this.unsubscribe()}
2. Redux使用
2.1 规范化Redux结构
index.js
- 创建store
- 传入reducer
import { createStore } from 'redux';// const { createStore } = require('redux'); // createRedux现已弃用const initialStore = {name: "Anonymous",
}const store = createStore(reducer)export default store;
actionsCreators.js
- 创建actions
// actions创建
export const changeNameAction = (name) => ({ type: 'change_name', name })
reducer.js
- 单独保存reducer
import { ADD_NUMBER, CHANGE_NAME } from "./constants";// reducer 是一个纯函数
// 接收两个参数 state: 之前的状态 action: 描述要产生的改变
export function reducer(state = initialStore, action) {switch (action.type) {case CHANGE_NAME:return { ...state, name: action.name };case ADD_NUMBER:return { name }default:return state;}
}
contants.js
- 保存type常量
// 常量
export const ADD_NUMBER = 'add_number'
export const CHANGE_NAME = 'change_name'
2.2 Redux的三大规则
- 单一数据源
- 整个应用程序的state被存储在一颗
object tree
中,并且这个object tree
只存储在一个store
中: Redux
并没有强制让我们不能创建多个Store,但是那样做并不利于数据的维护;- 单一的数据源可以让整个应用程序的state变得方便维护、追踪、修改;
- 整个应用程序的state被存储在一颗
- State是只读的
- 唯一修改State的方法一定是
触发action
,不要试图在其他地方通过任何的方式来修改State; - 这样就确保了View或网络请求都不能直接修改state,它们只能通过action来描述自己想要如何修改state;
- 这样可以保证所有的修改都被集中化处理,并且按照严格的顺序来执行,所以不需要担心
race condition(竟态)
的问题;
- 唯一修改State的方法一定是
- 使用纯函数来执行修改
- 通过reducer将旧state和actionsl联系在一起,并且返回一个新的State:
- 随着应用程序的复杂度增加,我们可以将reducer拆分成多个小的reducers,分别操作不同state tree的一部分;
- 但是所有的
reducer
都应该是纯函数,不能产生任何的副作用;
2.3 react-redux — 高阶组件封装库
之前讲到了高阶组件
,在实际开发中我们一般使用封装好的库直接调用,而不是自己封装高阶组件,现在来学习一下react-redux
这个高阶组件库,这个库主要是将react
和redux
联系在一起。
2.4 异步获取数据
从服务器获取数据我们可以选择在组件的componnetDidMount
中发送请求获取,但其实获取数据也是需要在redux
中去完成的。
所以我们选择在传递action
时先获取数据再传递到reducer
,但如果我们是这样写的:
axios.get("http://123.207.32.32:8000/home/multidata").then(res => {console.log(res.data);return { type: CHANGE_BANNERS, banners: [] }}).catch(err => {return { type: CHANGE_BANNERS, banners: [] }})
也就是获取数据后直接返回一个action对象,但其实返回的会是一个Promise对象,不符合redux
的规范(传入一个action
对象或者一个中间件函数)。
中间件我们需要使用到redux-thunk
这个库,首先进行安装:
npm i redux-thunk
const store = createStore(reducer, applyMiddleware(thunk));
之后就可以创建异步获取数据的action
// 异步获取数据的action
export const fetchHomeDataAction = () => {// 使用redux-thunk中间件,返回一个函数(内置dispatch和getState)return (dispatch, getState) => {axios.get("http://123.207.32.32:8000/home/multidata").then(res => {console.log(res.data, res.data.data.banner.list);dispatch(changeBannersAction(res.data.data.banner.list))})}
}
需要注意的是返回的函数中thunk
内置了dispatch
和getState
,这里只需要调用普通的action
修改数据即可。
2.5 react代码调试工具
首先安装这两个工具
- redux-devtools
- react-devtools
默认开发环境开启,生产环境关闭。
2.6 Redux模块拆分
3. RTK工具包
3.1 redux toolkit
- 在前面我们学习Redux的时候应该已经发现,redux的编写逻辑过于的繁琐和麻烦。
- 并且代码通常分拆在多个文件中(虽然也可以放到一个文件管理,但是代码量过多,不利于管理);
- ReduxToolkit包旨在成为编写Redux逻辑的标准方式,从而解决上面提到的问题;
- 在很多地方为了称呼方便,也将之称为"RTK”;
npm i @reduxjs/tookit react-redux
3.2 使用RTK
3.2.1 创建reducer片段 —createSlice
- name: 代替action的type;
- initialState: 初始值;
- reducers: 与之前的一样,但这里需要注意是函数
import { createSlice } from "@reduxjs/toolkit";const counterSlice = createSlice({name: 'counter',initialState: {counter: 888},reducers: {addNumber(state, action) {state.counter++}}
})// 导出的是counterSlice.reducer (不是counterSlice.reducers)
export default counterSlice.reducer
index.js
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from './modules/counter';const store = configureStore({reducer: {counter: counterReducer}
})export default store;