共计 7223 个字符,预计需要花费 19 分钟才能阅读完成。
Redux 的作用是什么
Redux
的作用在于实现 状态传递
、 状态治理
。在这里你可能会说了,如果是 状态传递
,那我props
的传递不也是能够达到这样的成果吗?context 上下文计划
不也是能够达到这样的成果吗?没错,是这样的,然而上述的两种计划是有 局限性
的。
props
计划只实用于父子组件传递状态。-
context
上下文计划尽管可能在根组件上定义上下文,然而有两种缺点- 只有上下文外面的状态产生扭转,就会从新渲染整个组件树,进而会产生宏大的性能开销。
- 组件的逻辑与状态的
耦合度
太高,不利于解耦,也就是无奈实现对状态的对立治理。
既然 Redux
的作用是对状态的治理与传递,那么他的作用场景呢?当然了你能够依据下面说的两种计划对 Redux
的应用做取舍,Redux
的实质就是全局变量被协调治理。
- 如果 波及多个状态,且多个状态会被多个组件应用到,比方商城购物车场景,你就能够毫不犹豫的思考用
Redux
。 - 如果 波及多个状态,然而状态虽多然而是用的组件惟一,或者有关联关系的组件应用 ,你就大可不必应用
Redux
,如果 状态不是那么多 ,那就更不用应用Redux
了。
除此之外,Redux
还有一个长处就是,不仅仅是 React
自身可能应用,就连别的框架,比方 jQuery
、kerry_dom
、vue
等都能够应用,然而比照于 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
文件外面的cloneDeep
、return state
、state = initialValue
。App.js
文件外面的dispatch
、getState
、type
、payload
。
很显著 createStore
的作用就是创立仓库,getState
为获得以后的 state
值,dispatch
为某个操作之后派发给 store
去更新某个 state
,type
为具体的某种交互,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
只实现 getState
、dispatch
、createStore
办法。
// 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
的大抵工作流如此: