# e-redux
**Repository Path**: ymcdhr/e-redux
## Basic Information
- **Project Name**: e-redux
- **Description**: redux基本示例、redux-saga异步请求示例、自定义中间件、代码组织结构示例等
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2021-09-14
- **Last Updated**: 2021-11-07
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# Redux
### redux 工作流程
view => dispatch => action => reducer => store

### redux 模块 => [redux store 的核心 api](https://www.redux.org.cn/docs/basics/Store.html)
1. createStore => 创建store
2. getState => 获取store
3. subscribe => 监听store
4. dispatch => 触发reducer

5. 核心 API 实现逻辑:createStore()
```js
// redux 原理,简版redux
// 参数1:reducer
// 参数2:initalState
// 参数3:注册中间件的函数
function createStore (reducer, preloadedState, enhancer) {
// reducer 类型判断
if (typeof reducer !== 'function') throw new Error('redcuer必须是函数');
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('enhancer必须是函数')
}
return enhancer(createStore)(reducer, preloadedState);
}
// 状态:通过 getState() 方法形成闭包,将局部变量留在内存中使用
var currentState = preloadedState;
// 订阅者
var currentListeners = [];
// 获取状态
function getState () {
return currentState;
}
// 用于触发action的方法
function dispatch (action) {
// 判断action是否是一个对象
if (!isPlainObject(action)) throw new Error('action必须是一个对象');
// 判断action中的type属性是否存在
if (typeof action.type === 'undefined') throw new Error('action对象中必须有type属性');
// 调用reducer函数 处理状态
currentState = reducer(currentState, action);
// 调用订阅者 通知订阅者状态发生了改变
for (var i = 0; i < currentListeners.length; i++) {
var listener = currentListeners[i];
listener();
}
}
// 订阅状态的改变
function subscribe (listener) {
currentListeners.push(listener);
}
// 默认调用一次dispatch方法 存储初始状态(通过reducer函数传递的默认状态)
dispatch({type: 'initAction'})
return {
getState,
dispatch,
subscribe
}
}
// 判断参数是否是对象类型
// 判断对象的当前原型对象是否和顶层原型对象相同
function isPlainObject (obj) {
if (typeof obj !== 'object' || obj === null) return false;
var proto = obj;
while (Object.getPrototypeOf(proto) != null) {
proto = Object.getPrototypeOf(proto)
}
return Object.getPrototypeOf(obj) === proto;
}
function applyMiddleware (...middlewares) {
return function (createStore) {
return function (reducer, preloadedState) {
// 创建 store
var store = createStore(reducer, preloadedState);
// 阉割版的 store
var middlewareAPI = {
getState: store.getState,
dispatch: store.dispatch
}
// 调用中间件的第一层函数 传递阉割版的store对象
var chain = middlewares.map(middleware => middleware(middlewareAPI));
var dispatch = compose(...chain)(store.dispatch);
return {
...store,
dispatch
}
}
}
}
```
### react-redux 模块 => 结合 react 和 redux
**react-redux 模块** 让组件中能够拿到 redux 的 store,提供 provider、connect 等方法
1. provider 将 react 入口组件包裹起来并注入 store;
2. connect 将 react 组件和 store 进行关联;
- // 1. connect 方法会帮助我们订阅store 当store中的状态发生更改的时候 会帮助我们重新渲染组件
- // 2. connect 方法可以让我们获取store中的状态 将状态通过组件的props属性映射给组件
- // 3. connect 方法可以让我们获取 dispatch 方法
**redux 的基本使用** 包括以下:
- **创建 store:** 在入口文件,定义默认初始化 initialStore 数据;并且将 store 绑定到 provider 上;
- **定义 action:** 对应的业务 action,定义使用哪个 reducer 更新数据;
- **定义 reducer:** 对应业务的 reducer,根据action,更新数据到 store;
- **绑定 store 到组件,** 在组件中触发 dispatch:组件中需要使用 connect 关联 store、组件和 actions,这里面做一些 map 映射方便调用 dispatch 使用 action。
### Redux 基本使用示例,[参考源码](https://gitee.com/ymcdhr/e-code/tree/master/04-02/4-2-1-redux/code/react-redux-guide)
#### 1. 定义stroe
```js
import { createStore, applyMiddleware } from "redux";
import RootReducer from "./reducers/root.reducer";
// import thunk from 'redux-thunk';
// import logger from "./middleware/logger";
// import test from "./middleware/test";
// import thunk from './middleware/thunk';
import createSagaMidddleware from 'redux-saga';
import rootSaga from './sagas/root.saga';
// 创建 sagaMiddleware
const sagaMiddleware = createSagaMidddleware();
// export const store = createStore(RootReducer, applyMiddleware(thunk));
export const store = createStore(RootReducer, applyMiddleware(sagaMiddleware));
// 启动 counterSaga
sagaMiddleware.run(rootSaga)
```
#### 2. 定义actions
```js
import { INCREMENT, DECREMENT, INCREMENT_ASYNC } from "../const/counter.const";
export const increment = payload => ({type: INCREMENT, payload});
export const decrement = payload => ({type: DECREMENT, payload});
export const increment_async = payload => ({type: INCREMENT_ASYNC, payload});
```
#### 3. 定义reducer、拆分reducer
```js
import { combineReducers } from 'redux';
import CounterReducer from './counter.reducer';
import ModalReducer from './modal.reducer';
// { counter: { count: 0 }, model: { show: false } }
export default combineReducers({
counter: CounterReducer,
modal: ModalReducer
})
```
```js
import { INCREMENT, DECREMENT } from "../const/counter.const";
const initialState = {
count: 0
}
export default (state = initialState, action) => {
switch(action.type) {
case INCREMENT:
return {
...state,
count: state.count + action.payload
}
case DECREMENT:
return {
...state,
count: state.count - action.payload
}
default:
return state;
}
}
```
#### 4. 主入口使用provider
```js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { Provider } from 'react-redux';
import { store } from './store';
ReactDOM.render(
// 通过provider组件 将 store 放在了全局的组件可以够的到的地方
,
document.getElementById('root')
);
```
#### 5. 在组件中使用数据
```js
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as couterActions from '../store/actions/counter.actions';
function Counter ({count, increment, decrement, increment_async}) {
return
{count}
}
// 1. connect 方法会帮助我们订阅store 当store中的状态发生更改的时候 会帮助我们重新渲染组件
// 2. connect 方法可以让我们获取store中的状态 将状态通过组件的props属性映射给组件
// 3. connect 方法可以让我们获取 dispatch 方法
const mapStateToProps = state => ({
count: state.counter.count
});
const mapDispatchToProps = dispatch => bindActionCreators(couterActions, dispatch)
// ==> 等同于
// const mapDispatchToProps = dispatch => ({
// increment_async () {
// dispatch(couterActions.increment_async)
// },
// decrement () {
// dispatch(couterActions.decrement)
// }
// })
export default connect(mapStateToProps, mapDispatchToProps)(Counter);
```
#### 6. subscribe 订阅事件的使用

```js
function select(state) {
return state.some.deep.property
}
let currentValue
function handleChange() {
let previousValue = currentValue
currentValue = select(store.getState())
if (previousValue !== currentValue) {
console.log('Some deep nested property changed from', previousValue, 'to', currentValue)
}
}
let unsubscribe = store.subscribe(handleChange)
unsubscribe()
```
### Redux 中间件
#### 1. 什么是中间件?

#### 2. 如何开发中间件?
- 在store中引入自定义中间件:index.js
```js
import { createStore, applyMiddleware } from "redux";
import thunk from './middleware/thunk';
export const store = createStore(RootReducer, applyMiddleware(thunk));
```
- 在中间件中对逻辑进行处理,然后执行action:middleware/thunk.js
```js
export default (store) => next => action => {
const { dispatch } = store
// 1. 当前这个中间件函数不关心你想执行什么样的异步操作 只关心你执行的是不是异步操作
// 2. 如果你执行的是异步操作 你在触发action的时候 给我传递一个函数 如果执行的是同步操作 就传递action对象
// 3. 异步操作代码要写在你传递进来的函数中
// 4. 当前这个中间件函数在调用你传递进来的函数时 要将dispatch方法传递过去
if (typeof action === 'function') {
return action(dispatch)
}
// next 是一个函数,调用执行 action
next(action)
}
// action 中定义异步操作 increment_async:返回一个 function,而不是对象
// export const increment_async = payload => dispath => {
// setTimeout(()=>{
// dispath({type: INCREMENT_ASYNC, payload})
// }, 2000)
// };
```
#### 3. 常用的中间件有哪些?
- redux-thunk => 在redux工作流中加入异步代码,但是异步代码需要卸载actions里面;
```js
// 1. 在store里面引入thunk:index.js
import { createStore, applyMiddleware } from "redux";
import thunk from 'redux-thunk';
export const store = createStore(RootReducer, applyMiddleware(thunk));
// 2. 在actions里面返回一个function:counter.actions.js
export const increment_async = payload => dispath => {
setTimeout(()=>{
dispath(increment(payload))
}, 2000)
};
```
- redux-sega => 在redux工作流中加入异步代码,允许将异步代码抽离到单独的文件中;
```js
// 1. 在store里面注册sega:index.js
import { createStore, applyMiddleware } from "redux";
import RootReducer from "./reducers/root.reducer";
import createSagaMidddleware from 'redux-saga';
import rootSaga from './sagas/root.saga';
// 创建 sagaMiddleware
const sagaMiddleware = createSagaMidddleware();
export const store = createStore(RootReducer, applyMiddleware(sagaMiddleware));
// 启动 counterSaga
sagaMiddleware.run(rootSaga)
// 2. 创建一个单独的文件来写异步代码:counter.saga.js
import { takeEvery, put, delay } from 'redux-saga/effects';
import { increment } from '../actions/counter.actions';
import { INCREMENT_ASYNC } from '../const/counter.const';
// takeEvery 接收 action
// put 触发 action
function* increment_async_fn (action) {
yield delay(2000);
yield put(increment(action.payload))
}
export default function* counterSaga () {
// 接收action
yield takeEvery(INCREMENT_ASYNC, increment_async_fn)
}
// 3. actions中定义:counter.actions.js
export const increment_async = payload => ({type: INCREMENT_ASYNC, payload});
// 4. saga的代码拆分:root.saga.js
import { all } from 'redux-saga/effects';
import counterSaga from './counter.saga';
import modalSaga from './modal.saga';
export default function* rootSaga () {
yield all([
counterSaga(),
modalSaga()
])
}
```
- redux-actions 简化 action 和 reducer 中的代码
#### 4. Redux 中间件的实现原理
1. 中间件的作用:
- 正常的流程:触发 dispatch(action) 之后,reducer 接收到 action 更新 store;
- 中间件流程:触发 dispatch(action) 之后,中间件接收到 action 然后执行所有中间件(在 reducer 接受到 action 之前,本质上市对 dispatch 进行增强),然后中间件中执行最后的 next() 传递给 reducer(或者中间件中触发 dispatch(action) 调用正常的 action,传递给 reducer)
```
dispatch(action) => store(执行中间件函数) => next(action) => action => reducer
dispatch(action) => store(执行中间件函数) => dispatch(action) => action => reducer
```
2. 中间件的创建:三层函数;
- (1)logger 的 next 实际上就是 thunk 最里层的那个函数
- (2)thunk 的 next 实际上是最后的中间件函数
```js
// 定义中间件1:logger
function logger (store) {
return function (next) {
return function (action) {
console.log("logger")
next(action)
}
}
}
// 定义中间件1:thunk
function thunk(store) {
return function (next) {
// 最里层的才是中间件函数,对应 logger 的 next
return function (action) {
console.log("thunk")
next(action)
}
}
}
// 注册中间件,执行顺序:logger => thunk => reducer
export const store = createStore(RootReducer, applyMiddleware(logger, thunk));
```
2. 类似逻辑源码

---
## redux-saga
#### redux-saga 的流程
```
1. 页面组件调用异步Action =>
2. Saga接受异步Action(Store中注册了Saga) =>
3. Saga调用异步方法:async_getUsers_fn =>
4. Saga异步方法中调用更新数据的同步Action:updateUsers =>
5. Reducer根据指定Action更新State =>
6. 页面组件刷新数据
```
1. Page 页面组件中调用异步action:async_getUsers
```js
```
2. Action 中定义了异步action:async_getUsers
```
// 异步 action,使用 redux-saga 示例
export const async_getUsers = (url) => {
return {
type: ASYNC_GETUSERS,
url
}
}
```
3. Store 页面注册了saga中间件,所以调用异步action时会执行对应saga:
```
// Store:
sagaMiddleware.run(pageSaga)
```
4. Saga 中定义了接受对应的action:async_getUsers/ASYNC_GETUSERS;接受action时执行执行异步函数:async_getUsers_fn
```js
// 执行异步操作:
// 在 saga 中执行异步操作不用 setTimeout,为什么?而是用 delay
function* async_getUsers_fn (action) {
yield delay(2000)
// 通过 action.url 接受组件传过来的参数
const { data } = yield axios.get(action.url)
console.log("users:", data)
yield put(updateUsers(data))
}
// pageSaga:
// 导出 Generator 函数
export default function* pageSaga (url) {
// 接受 action 的 type
yield takeEvery(ASYNC_GETUSERS, async_getUsers_fn)
}
```
5. Saga 中执行异步函数时,执行同步的action更新store;其实调用的更新数据的action:updateUsers
```
yield put(updateUsers(data))
```
6. Reducer根据指定Action更新State,页面组件重新渲染;
#### redux-saga 的传参(页面组件向saga异步方法传参)
1. 页面组件传参给异步action
```js
```
2. 异步action接收参数并传递:
```
// 异步 action,使用 redux-saga 示例
export const async_getUsers = (url) => {
return {
type: ASYNC_GETUSERS,
url
}
}
```
3. Saga 回调函数中接受参数:
```
// 执行异步操作:
// 在 saga 中执行异步操作不用 setTimeout,为什么?而是用 delay
function* async_getUsers_fn (action) {
yield delay(2000)
// 通过 action.url 接受组件传过来的参数
const { data } = yield axios.get(action.url)
console.log("users:", data)
yield put(updateUsers(data))
}
```
#### redux-saga 的合并文件【待整理】
---
## redux 的调试方法
#### redux 的调试思想
1. 总体来说,还是按照数据流的流动来进行调试
2. // ... 【遗留问题,待整理】
---
## Redux 的源码解读
---
## Redux 可能的面试题
### Redux 的使用
1. 入口使用 provider 包裹 store
2. 创建store,传入reducer作为参数,注册中间件
3. 定义reducer
4. 定义action
5. 页面组件中,使用connect绑定store、dispatch等
### Redux 的使用/数据流流程
页面组件 => dispatch(action) => action => reducer(state, action) => state(新的state)
### Redux 的中间件流程
页面组件 => dispatch(action) => action => store(之前注册了中间件函数,此时执行中间件函数) => next(action) => action => reducer
### Redux-saga 的执行流程
### Redux 的中间件如何开发
三层函数

### Redux 的中间件流的实现原理
利用 compose 函数组合 + 循环调用(传参:next为中间件函数)