网上Redux入门教程很多,但是为啥这么使用,却百思不得解。今天写一篇关于Redux的文章,帮助大家了解Redux为啥这么写,以及内部做了啥。
目录:
- types
- actions.ts
- middleware.ts
- reducers.ts
- store.ts
- utils
- actionTypes.ts
- formatProdErrorMessage.ts
- isPlainObject.ts
- kindOf.ts
- symbol-observable.ts
- warning.ts
- applyMiddleware.ts
- bindActionCreators.ts
- combineReducers.ts
- compose.ts
- createStore.ts
- index.ts
applyMiddleware.ts
将所有中间件合并成一个数组,然后依次执行,用法如下:
const store = applyMiddleware([ loggerMiddleware, thunkMiddleware, ...others ])(createStore)(reducer, preloadState)
复制代码
源码如下:
export default function applyMiddleware (...middlewares) {
return (createStore) => (reducer, preloadState) => {
const store = createStore(reducer, preloadState)
let dispatch = () => {}
const middlewareAPI = {
getState: store.getState,
dispatch: (action, ...args) => dispatch(action, ...args)
}
const chain = middlewares.map(middleware => middleware(middlewareAPI))
// compose 是 src 目录下的compose方法,暂时不用管
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
复制代码
bindActionCreators.ts
将一个或多个action和dispatch组合起来生成mapDispatchToProps需要生成的内容,目的就是简化书写,减轻开发负担。用法如下:
// 使用前
import React, { useCallback } from "react";
import { createStore, bindActionCreators } from "redux";
import { Provider, connect } from "react-redux";
// 子组件
function Child({ msg, onClick }) {
const onUserClick = useCallback(() => {
onClick("我被点击了");
}, [onClick]);
return <div onClick={onUserClick}>被点击了吗? {msg}</div>;
}
// 让子组件使用redux
const ChildWrap = connect(
state => ({ msg: state.msg }),
dispatch => ({
onClick: payload => dispatch({ type: "CLICK", payload })
})
)(React.memo(Child));
export default function App() {
const store = createStore((state = {}, action) => {
switch (action.type) {
case "CLICK":
state = { msg: action.payload };
break;
default:
state = {};
break;
}
return state;
});
return (
<Provider store={store}>
<div className="App">
<ChildWrap />
</div>
</Provider>
);
}
// 使用后
import * as actionCreators from './actionCreators'
const ChildWrap = connect(
(state) => ({ msg: state.msg }),
(dispatch, ownProps) => bindActionCreators(actionCreators, dispatch)
)(React.memo(Child));
// actionCreators.js
export const onClick = (payload) => ({
type: "CLICK",
payload
});
复制代码
源码如下:
/**
* 批量处理action
* @actionCreators { Function | Object } 如果是多个action,则需要传入对象
* @dispatch { Function } store的dispatch对象
*/
export default function bindActionCreators (actionCreators, dispatch) {
// 如果是单个函数
if (typeof actionCreators === 'function') {
return bindActionCreator(actionCreators, dispatch)
}
const boundActionCreators = {}
for (const key in actionCreators) {
const actionCreator = actionCreators[key]
if (typeof actionCreator === 'funciton') {
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
}
}
return boundActionCreators
}
/**
* 处理单个action
* @param actionCreators { Function | Object } 如果是多个action,则需要传入对象
* @param dispatch { Function } store的dispatch对象
*/
export default function bindActionCreator (actionCreator, dispatch) {
return function (this, ...args) {
return dispatch(actionCreator.bind(this, ...args))
}
}
复制代码
combineReducers.ts
使reducer结合到一起。即将各个子reducer合并成一个大的reducer合并后的reducer可以调用各个子 reducer,并把它们返回的结果合并成一个state对象。
参数: 一个Object对象,key为 reducerName 可以自定义,value为 reducer函数
返回值: 调用所有传入的reducer,即传入参数对象的 value 值。返回和传入参数结构一致的state对象
import React, { useCallback } from "react";
import { createStore, combineReducers } from "redux";
import { Provider, connect } from "react-redux";
function Child(props) {
const { msg, onClick } = props
const onUserClick = useCallback(() => {
onClick("sssss");
}, [onClick]);
return <div onClick={onUserClick}>11111 {msg}</div>;
}
const ChildWrap = connect(
// 注意此处的state.reduce1对应combineReducer中写的key
(state) => ({ msg: state.reducer1.msg }),
(dispatch) => ({
onClick: (payload) => dispatch({ type: "CLICK", payload })
})
)(Child);
export default function App() {
const reducer1 = (state = {}, action) => {
switch (action.type) {
case "CLICK":
state = { msg: action.payload };
break;
default:
state = {};
break;
}
return state;
};
const reducer2 = (state = {}, action) => {
switch (action.type) {
case "CLICK2":
state = { msg: action.payload };
break;
default:
state = {};
break;
}
return state;
};
const store = createStore(combineReducers({ reducer1, reducer2 }));
return (
<Provider store={store}>
<div className="App">
<ChildWrap />
</div>
</Provider>
);
}
复制代码
源码如下:
export default function combineReducers (reducers) {
const reducerKeys = Object.keys(reducers)
const finalReducers = {}
// 剔除reducer中非方法的内容
for (let i = 0; i < reducerKeys.length; i ++) {
const key = reducerKeys[i]
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key]
}
}
const finalReducerKeys = Object.keys(finalReducers)
// 组装在一起,相当于一个大的reducer,入参也和reducer一致
return function combination (state = {}, action) {
let hasChanged = false
const nextState = {}
for (let i = 0; i < finalReducerKeys.length; i ++) {
const key = finalReducerKeys[i]
const reducer = finalReducerKeys[key]
const previousStateForKey = state[key]
let nextStateForKey = reducer(previousStateForKey, action)
nextState[key] = nextStateForKey
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
hasChanged = hasChanged || finalReducerKeys.length !== Object.keys(state).length
return hasChanged ? nextState : state
}
}
复制代码
compose.ts
函数式编程里常用的组合函数,将多个函数组合成一个函数依次执行。个人认为这个函数是redux整个源码中最值得一看的代码,而且可以脱离redux中,在日常开发中使用。
export default function compose (...funcs) {
if (funcs.length === 0) {
return () => {}
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
复制代码
createStore.ts
相信这个方法绝对是大家用redux最多的一个方法,直接上源码
/**
* 创建store的方法
*
* @param reducer 纯函数,接收store和action,返回新store
* @param preloadedState 初始化的state值
* @param enhancer store增强函数,类似于applyMiddleware
*/
export default function createStore (reducer, preloadedState, enhancer) {
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState
preloadedState = null
}
if (typeof enhancer !== 'undefined' && typeof enhancer === 'function') {
return enhancer(createStore)(reducer, preloadedState)
}
let currentState = preloadedState
let currentReducer = reducer
let currentListeners = []
let nextListeners = currentListeners
let isDispatching = false
/**
* 生成currentListeners的浅层副本,使用时可以将nextListeners用作临时列表。
*/
function ensureCanMutateNextListeners () {
if (currentListeners === nextListeners) {
nextListeners = currentListeners.splice()
}
}
/**
* 获取最新的状态
*/
function getState () {
return currentState
}
/**
* 添加更改侦听器。它将在任何时候调用一个动作,并且状态树的某些部分可能已经更改。然后可以调
* 用“getState()”来读取回调中的当前状态树。
*/
function subscribe (listener) {
let isSubscribed = true
ensureCanMutateNextListeners()
nextListeners.push(listener)
return function unSubscribe () {
if (!isSubscribed) {
return
}
isSubscribed = false
ensureCanMutateNextListeners()
const index = nextListeners.indexOf(listener)
nextListeners.splice(index, 1)
currentListeners = null
}
}
/**
* 派发事件,触发reducer改变state的值。这是唯一可以修改state值的方法
*/
function dispatch (action) {
if (typeof action.type === 'undefined') {
throw new Error(
'Actions may not have an undefined "type" property. You may have misspelled an action type string constant.'
)
}
if (isDispatching) {
throw new Error('Reducers may not dispatch actions.')
}
try {
isDispatching = true
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}
const listeners = (currentListeners = nextListeners)
for (let i = 0; i < listeners.length; i ++) {
const listener = listeners[i]
listener()
}
return action
}
function observable() {
const outerSubscribe = subscribe
return {
subscribe(observer: unknown) {
function observeState() {
const observerAsObserver = observer as Observer<S>
if (observerAsObserver.next) {
observerAsObserver.next(getState())
}
}
observeState()
const unsubscribe = outerSubscribe(observeState)
return { unsubscribe }
},
[$$observable]() {
return this
}
}
}
/**
* 替换当前当前用于计算的reducer
*/
function replaceReducer (nextReducer) {
currentReducer = nextReducer
dispatch({ type: ActionTypes.REPLACE })
return store
}
// 所以明白了,reducer中的switch的default开始会执行一次了吧
dispatch({ type: ActionTypes.INIT })
const store = {
dispatch,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
}
return store
}
复制代码
index.ts
导出上面的方法
types
定义了整个项目中需要使用的interface和type,篇幅原因,不再详细说明,有兴趣可以查看源码
utils
工具类方法,篇幅原因,不再详细说明,有兴趣可以查看源码
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END