关于react.js:深度理解Redux原理并实现一个redux

62次阅读

共计 7223 个字符,预计需要花费 19 分钟才能阅读完成。

Redux 的作用是什么

Redux的作用在于实现 状态传递 状态治理 。在这里你可能会说了,如果是 状态传递 ,那我props 的传递不也是能够达到这样的成果吗?context 上下文计划 不也是能够达到这样的成果吗?没错,是这样的,然而上述的两种计划是有 局限性 的。

  • props计划只实用于父子组件传递状态。
  • context上下文计划尽管可能在根组件上定义上下文,然而有两种缺点

    • 只有上下文外面的状态产生扭转,就会从新渲染整个组件树,进而会产生宏大的性能开销。
    • 组件的逻辑与状态的 耦合度 太高,不利于解耦,也就是无奈实现对状态的对立治理。

既然 Redux 的作用是对状态的治理与传递,那么他的作用场景呢?当然了你能够依据下面说的两种计划对 Redux 的应用做取舍,Redux的实质就是全局变量被协调治理。

  • 如果 波及多个状态,且多个状态会被多个组件应用到,比方商城购物车场景,你就能够毫不犹豫的思考用Redux
  • 如果 波及多个状态,然而状态虽多然而是用的组件惟一,或者有关联关系的组件应用 ,你就大可不必应用Redux,如果 状态不是那么多 ,那就更不用应用Redux 了。

除此之外,Redux还有一个长处就是,不仅仅是 React 自身可能应用,就连别的框架,比方 jQuerykerry_domvue 等都能够应用,然而比照于 vue 来讲的话,vue 有本人比拟好的的状态治理库 vuex,好了废话不多说了,咱们先来看看Redux 在我的项目中是如何是用的。

Redux 的应用

// store.js
import {createStore} from "redux";
import reducer from "./reducer";
export default createStore(reducer);
// reducer.js
import {cloneDeep} from 'lodash';
const initilaValue = {count: 0};
const reducer = (state = initilaValue, action) => {state = cloneDeep(state)
const {type, payload} = action;
switch (type) {
case "add":
state.count += payload;
break;
case "reduce":
state.count -= payload;
break
default:
}
return state;
};
export default reducer;
// App.js
import React, {Component} from 'react';
import store from "./store";
export default class App extends Component {componentDidMount() {
//reducer 不会触发页面变动,须要 state 来触发
store.subscribe(() =>{this.setState(store.getState())
})
}
render() {
// 获取 reducer 数据
const {count} = store.getState()
return (
<div>
<div type='primary' onClick={this.reduce}>-</div>
<span>{count}</span>
<div type='primary' onClick={this.add}>+</div>
</div>
);
}
reduce = () => {
// 告诉 reducer 页面数据变动了
store.dispatch({
type: 'reduce',
payload: 1
})
}
add = () => {
// 告诉 reducer 页面数据变动了
store.dispatch({
type: 'add',
payload: 1
})
}
}

上述代码就能够实现 count 的加减计算了,咱们能够看到有几处须要留神的中央。

  • store.js文件外面的createStore
  • reducer.js文件外面的cloneDeepreturn statestate = initialValue
  • App.js文件外面的dispatchgetStatetypepayload

很显著 createStore 的作用就是创立仓库,getState为获得以后的 state 值,dispatch为某个操作之后派发给 store 去更新某个 statetype 为具体的某种交互,payload为每次交互的具体内容。各位同学能够看失去我在 reducer 中做了一次 state 的深克隆,这是为什么呢?是因为在每一次的 action 中咱们拿到的是同一个 state内存地址 ,咱们的冀望是不论你在switch 中如何更改 state 然而我不心愿在这一步就扭转了公共状态中的 count,只有在我return 的时候才会去更改真正的公共状态,也就是说 reducer 函数执行产生的公有闭包外面的公共状态信息。而 state = initialValue 这一步的操作就是第一次派发的时候,reducer接管的 state 为空,咱们把根底值赋给它。理解了这么多,咱们还是去看一下他的源码是如何实现的吧。

Redux 的源码

//Redux/redux/src/index.ts
export {
createStore, // 创立仓库 api
combineReducers, // 合并 Reducer
bindActionCreators, // 转化 action
applyMiddleware, // 中间件利用计划
compose,// 函数组合
__DO_NOT_USE__ActionTypes
}

下面的 index.ts 文件夹外面暴露出了几个 api,咱们次要针对createStore 看看。

export default function createStore(reducer: Reducer<S, A>, preloadedState?: PreloadedState<S> | StoreEnhancer<Ext, StateExt>, enhancer?: StoreEnhancer<Ext, StateExt>): Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext {
// createStore 函数不反对第二三四参数为函数
// 详见 https://redux.js.org/tutorials/fundamentals/part-4-store#creating-a-store-with-enhancers
if ((typeof preloadedState === 'function' && typeof enhancer === 'function') ||
(typeof enhancer === 'function' && typeof arguments[3] === 'function')
) {
throw new Error(...)
}
// 第二参数是函数,且第三参数不传
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState as StoreEnhancer<Ext, StateExt>
preloadedState = undefined
}
// 有第三参数且不是函数
if (typeof enhancer !== 'undefined') {if (typeof enhancer !== 'function') {
throw new Error(`Expected the enhancer to be a function. Instead, received: '${kindOf( enhancer)}'`
)
}
return enhancer(createStore)(
reducer,
preloadedState as PreloadedState<S>
) as Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext
}
// 如果 reducer 不是函数,要报错
if (typeof reducer !== 'function') {
throw new Error(`Expected the root reducer to be a function. Instead, received: '${kindOf( reducer)}'`
)
}
let currentReducer = reducer // 以后的 reducer
let currentState = preloadedState as S // 以后的 state
let currentListeners: (() => void)[] | null = [] // 事件池
let nextListeners = currentListeners
let isDispatching = false // 正在派发
/** * This makes a shallow copy of currentListeners so we can use * nextListeners as a temporary list while dispatching. * * This prevents any bugs around consumers calling * subscribe/unsubscribe in the middle of a dispatch. */
function ensureCanMutateNextListeners() {if (nextListeners === currentListeners) {nextListeners = currentListeners.slice()
}
}
// 返回以后的 state
function getState(): S {if (isDispatching) {
throw new Error(...)
}
return currentState as S
}
// 向事件池中增加更新事件
function subscribe(listener: () => void) {
// 校验是不是函数
if (typeof listener !== 'function') {
throw new Error(...)
}
if (isDispatching) {
throw new Error('You may not call store.subscribe() while the reducer is executing.' +
'If you would like to be notified after the store has been updated, subscribe from a' +
'component and invoke store.getState() in the callback to access the latest state.' +
'See https://redux.js.org/api/store#subscribelistener for more details.'
)
}
let isSubscribed = true
// 防止反复增加
ensureCanMutateNextListeners()
nextListeners.push(listener)
//subscribe 函数每执行一次,都会返回一个 unsubscribe 用来从事件池中移除以后事件
return function unsubscribe() {if (!isSubscribed) {return}
if (isDispatching) {
throw new Error(...)
}
isSubscribed = false
ensureCanMutateNextListeners()
// 移除以后事件
const index = nextListeners.indexOf(listener)
nextListeners.splice(index, 1)
currentListeners = null
}
}
// 派发函数
function dispatch(action: A) {
// 如果传过来的 action 不是对象报错
if (!isPlainObject(action)) {
throw new Error(...)
}
// 每一个 action 中都须要一个 type 字段,没有就报错
if (typeof action.type === 'undefined') {
throw new Error(...)
}
// 正在派发中..
if (isDispatching) {throw new Error('Reducers may not dispatch actions.')
}
try {
isDispatching = true
// 执行 reducer,扭转 state
currentState = currentReducer(currentState, action)
} finally {isDispatching = false}
// dispatch 告诉事件池去执行事件,遍历执行
const listeners = (currentListeners = nextListeners)
for (let i = 0; i < listeners.length; i++) {const listener = listeners[i]
listener()}
return action
}
// 替换 replaceReducer
function replaceReducer<NewState, NewActions extends A>(nextReducer: Reducer<NewState, NewActions>): Store<ExtendState<NewState, StateExt>, NewActions, StateExt, Ext> & Ext {
// 入参不是函数,报错
if (typeof nextReducer !== 'function') {
throw new Error(...)
}
...
// 刚开始进来要派发一次,同步 state,其中 {type: ActionTypes.REPLACE} 为惟一标识
// 假使不为惟一标识的话,那可能一开始就毁坏了状态 value
dispatch({type: ActionTypes.REPLACE} as A)
// change the type of the store by casting it to the new store
return store as unknown as Store<
ExtendState<NewState, StateExt>,
NewActions,
StateExt,
Ext
> &
Ext
}
...
// 初始化 store 的时候,须要派发一次同步 state
dispatch({type: ActionTypes.INIT} as A)
const store = {
dispatch: dispatch as Dispatch<A>,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
} as unknown as Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext
// 返回仓库 const store = createStore({count:0})
return store
}

参考 React 实战视频解说:进入学习

的确短短几百行代码实现了 redux,为此咱们也来实现一个简易版的redux 示意敬意,咱们的 redux 只实现 getStatedispatchcreateStore 办法。

// myRedux
import {cloneDeep} from 'lodash'
export function createStore(reducer) {if(typeof reducer !== 'function') {throw new Error('reducer must be an function')
}
let state,
listeners = [];
const getState = () => {
// 深克隆一个 state
return cloneDeep(state);
}
const subscribe = (listener) => {if(typeof listener !== 'function'){throw new Error('listener must be an function')
}
// 去重
if(!listeners.includes(listener)){listeners.push(listener)
}
// 源码外面执行 subscribe, 返回一个卸载函数
return function unsubscribe(){listeners.filter(action => action!== listener)
}
}
const dispatch = (action) => {if(typeof action !== 'object' && action !== null){throw new Error('action must be a object')
}
// 判断有没有 type
if(typeof action.type === undefined){throw new Error('action.type must existence')
}
// 执行
try {state = reducer(state, action)
} catch (error) {//...}
// 告诉事件池中的办法执行
listeners.forEach(listener=>{if(typeof listener === 'function'){listener();
}
})
}
// 第一次进来要派发一次,同步初始状态
dispatch({type:typeof Symbol !== undefined ? Symbol('ABC') : '惟一值'
})
// 暴露出办法
return {
getState,
dispatch,
subscribe
}
}

各位能够去 codeSandBox 下面去实际一下,当然了这只是简易版的redux,官网举荐应用 react-redux 来进行理论的我的项目开发,因为他只关注于数据管理。

总结

redux的大抵工作流如此:

正文完
 0