学习redux从以下几个方面入手
redux是什么、为什么需要redux、Redux原理、Redux使用、实现一个简单的Redux
1.redux是什么
- Redux 是一个用于 JavaScript 应用程序的状态管理库,尤其是在构建 React 应用时常用。它提供了一个中心化的方式来管理应用的状态,并确保不同的组件可以共享和更新这些状态。Redux 的设计思想来源于 Flux 架构,它通过单一数据源(Store)来管理所有状态,并且确保状态的变化是可预测的。
2.为什么需要Redux
- 状态集中管理
- 状态管理的可预测性
- 方便调试
- 跨组件共享状态
- 状态变化的明确性
- 解耦和可测试
- 方便与其他库做集成(中间件功能)
3.Redux原理
3.1.Redux核心原理-store
- store相当于一个容器用于存储你需要修改的数据
3.2.Redux核心原理-action
- 相当于执行的一个动作,比如加+1、减-1。指定一个修改数据的动作
- 所有的数据变化都需要用dispatch来派发一个action来进行更新数据
action是一个普通的JavaScript对象,用来描述这次更新的type和content
3.3.Redux的核心理念-reducer
- 是用来链接数据state 和动作 action
3.4.Redux的三大原则
- 单一数据源
- 整个应用程序的state是会被到一个Object树上,并且这个Object树只有一个store
- 这个store上会提供
dispatch、getState、subscribe方法
- state只读性
- 唯一可以修改的state的方法是触发action
- 使用纯函数来修改
- 通过
reducer将旧state和action联系在一起, 并且返回一个新的state - 随着应用程序的复杂度增加,我们可以将
reducer拆分成多个小的reducers,分别操作不同state tree的一部分 - 但是所有的
reducer都应该是纯函数,不能产生任何的副作用
- 通过
4.Redux使用
-
安装Redux
npm i redux -
创建一个对象, 作为我们要保存的状态
-
创建
Store来存储这个state- 创建
store时必须创建reducer - 我们可以通过
store.getState来获取当前的state
- 创建
-
通过
action来修改state- 通过
dispatch来派发action - 通常
action中都会有type属性,也可以携带其他的数据
- 通过
-
修改
reducer中的处理代码- 这里一定要记住,
reducer是一个纯函数,不能直接修改state - 后面会讲到直接修改
state带来的问题
- 这里一定要记住,
-
可以在派发
action之前,监听store的变化
import { legacy_createStore as createStore } from 'redux';
let initState = {
number: 0,
};
function reducers(state = initState, action) {
switch (action.type) {
case 'ADD':
return { number: state.number + 1 };
case 'SUB':
return { number: state.number - 1 };
default:
return state;
}
}
let store = createStore(reducers);
// 4.action
const action1 = { type: 'ADD' };
const action2 = { type: 'SUB', num: 2 };
// 5.订阅store的修改
store.subscribe(() => {
console.log('state发生了改变: ', store.getState().number);
});
// 6.派发action
store.dispatch(action1);
store.dispatch(action2);
5.Redux 实现
5.1.实现createStroe
- createStore.js
/**
* 创建一个Redux store来管理和更新应用的状态
*
* @param {Function} reducer 用于处理动作并更新状态的 reducer 函数
* @param {any} preloadState 初始状态,可选参数
* @returns {Object} 包含 dispatch、subscribe 和 getState 三个方法的 store 对象
*/
export default function createStore(reducer, preloadState) {
// 当前状态,初始化为预加载状态
let currentState = preloadState
// 当前的监听器数组,用于存储订阅的监听器函数
let currentListeners = [];
console.log(currentState)
/**
* 获取当前状态
*
* @returns {any} 当前的状态
*/
function getState() {
return currentState
}
/**
* 订阅一个监听器,当状态发生变化时会自动调用
*
* @param {Function} listener 监听器函数,无参数
* @returns {Function} 用于取消订阅的函数
*/
function subscribe(listener) {
currentListeners.push(listener)
// 返回一个用于取消订阅的函数
return function unsubscribe() {
const index = currentListeners.indexOf(listener);
currentListeners.splice(index, 1);
};
}
/**
* 发送一个动作到 store,触发状态更新
*
* @param {Object} action 动作对象,必须包含 type 属性
* @returns {Object} 动作对象,用于链式调用
*/
function dispatch(action) {
// 检查动作对象是否为纯对象
if (Object.getPrototypeOf(action) !== Object.prototype) {
throw new Error(`动作必须是一个纯对象,如果想进行异步操作请使用中间件`);
}
// 检查动作对象是否包含 type 属性
if (typeof action.type === "undefined") {
throw new Error(`动作不能一个值为undefined的type属性`);
}
// 使用 reducer 函数处理动作并更新当前状态
currentState = reducer(currentState, action);
// 调用所有订阅的监听器函数通知状态更新
for (let i = 0; i < currentListeners.length; i++) {
const listener = currentListeners[i];
listener();
}
}
// 初始化时发送一个特殊动作来设置初始状态
dispatch({ type: '@@redux/INIT' });
// 返回包含 dispatch、subscribe 和 getState 方法的 store 对象
return {
dispatch,
subscribe,
getState
};
}
5.2.实现bindActionCreators
/**
* 创建一个绑定dispatch的action创建函数
* 这个函数的目的是将一个普通的action创建函数转换为可以直接在组件中调用的函数
* 它通过应用apply方法传递所有的参数,并自动调用dispatch方法来分发action
*
* @param {Function} action - 一个普通的action创建函数,它生成一个将被分发的action对象
* @param {Function} dispatch - Redux store的dispatch方法,用于分发action
* @returns {Function} - 返回一个新的函数,当调用这个新函数时,它会调用传入的action创建函数
* 并自动将结果action对象分发到Redux store
*/
function bindActionCreator(action, dispatch) {
return function () {
return dispatch(action.apply(this, arguments))
}
}
/**
* 将action创建函数与dispatch方法绑定
*
* 此函数旨在处理两种情况:
* 1. 当传入的actions是一个函数时,直接绑定dispatch
* 2. 当传入的actions是一个对象时,遍历其所有属性,将函数类型的action创建函数绑定dispatch
*
* @param {Object|Function} actions - 一个包含多个action创建函数的对象,或单个action创建函数
* @param {Function} dispatch - Redux的dispatch方法,用于触发state更新
* @returns {Object|Function} - 绑定dispatch后的action创建函数或对象
*/
export default function bindActionCreators(actions, dispatch) {
// 判断传入的actions是否为函数,若是,则直接绑定dispatch
if (typeof actions === 'function') {
return bindActionCreator(actions, dispatch)
}
// 创建一个空对象用于存储绑定后的action创建函数
const boundActionCreators = {}
// 遍历actions对象的所有属性
for (const key in actions) {
// 获取当前属性的值,即可能的action创建函数
const actionCreator = actions[key]
// 判断当前属性值是否为函数,若是,则将其与dispatch绑定,并存入结果对象
if (typeof actionCreator === 'function') {
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
}
}
// 返回包含所有绑定后action创建函数的对象
return boundActionCreators
}
5.3.实现combineReducers
/**
* 导出一个默认的高阶reducer生成函数
* 该函数的目的是将多个子reducer组合成一个单一的reducer函数
* 这样可以简化reducer的管理,并确保每个子reducer都能处理特定的状态片段
*
* @param {Object} reducers - 包含多个子reducer的函数对象
* @returns {Function} - 返回一个组合了所有子reducer功能的单一reducer函数
*/
export default function combineReducers(reducers) {
// 获取所有子reducer的键名数组
const reducerKeys = Object.keys(reducers)
/**
* 返回一个组合reducer函数,它会依次调用每个子reducer来处理状态
*
* @param {Object} state - 当前的应用状态对象
* @param {Object} action - 发出的动作对象,用于描述发生的事情
* @returns {Object} - 返回一个新的状态对象,该对象是所有子reducer处理后的结果
*/
return function combination(state = {}, action) {
// 初始化下一个状态对象,用于存储所有子reducer处理后的状态
const nextState = {}
// 遍历每个子reducer,让它们依次处理自己的状态片段
for (let i = 0; i < reducerKeys.length; i++) {
// 获取当前子reducer的键名
const key = reducerKeys[i];
// 获取当前子reducer函数
const reducer = reducers[key];
// 获取当前状态下的子状态
const previousStateForKey = state[key];
// 调用子reducer处理状态,并获取处理后的子状态
const nextStateForKey = reducer(previousStateForKey, action);
// 将处理后的子状态放入下一个状态对象中
nextState[key] = nextStateForKey;
}
// 返回组合了所有子状态的下一个状态对象
return nextState;
}
}
5.4.实现applyMiddleware
import compose from "./compose";
/**
* 导出一个默认的高阶函数applyMiddleware,用于将中间件应用到Redux store中
* @param {...functions} middlewares - 一个或多个中间件函数
* @returns {function} - 返回一个高阶函数,该函数接受createStore作为参数,并返回一个新的加强版的createStore函数
*/
export default function applyMiddleware(...middlewares) {
// 返回一个高阶函数,用于包装原始的createStore函数
return createStore => (reducer, preloadedState) => {
// 使用原始的createStore函数创建store
let store = createStore(reducer, preloadedState);
// 初始化dispatch方法为store的dispatch方法
let dispatch = store.dispatch
// 初始化一个空数组,用于存储中间件链
let chain = []
// 创建一个middlewareAPI对象,提供getState和dispatch方法给中间件使用
let middlewareAPI = {
getState: store.getState,
dispatch: (...action) => dispatch(...action)
}
// 使用map函数和middlewareAPI,将所有中间件构建成一个中间件链
chain = middlewares.map(middleware => middleware(middlewareAPI));
// 使用compose函数组合中间件链,并增强store的dispatch方法
dispatch = compose(...chain)(store.dispatch);
// 返回一个新的store对象,包括原始store的所有属性和新的dispatch方法
return { ...store, dispatch }
}
}
/**
* 函数组合工具函数
* 该函数接受多个函数作为参数,并返回一个新函数
* 新函数的执行效果等同于依次执行每个传入的函数
* 如果没有提供任何函数,则返回一个恒等函数
* 如果只提供了一个函数,则直接返回该函数
* 对于多个函数,使用reduce方法将它们组合成一个函数
* @param {Function[]} funcs - 需要组合的函数数组
* @returns {Function} - 组合后的函数
*/
export default function compose(...funcs) {
// 当没有提供任何函数时,返回一个恒等函数
if (funcs.length === 0) {
return arg => arg
}
// 当只提供了一个函数时,直接返回该函数
if (funcs.length === 1) {
return funcs[0]
}
// 使用reduce方法将多个函数组合成一个函数
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}