持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第16天,点击查看活动详情
前言
哈喽大家好,我是Lotzinfly,一位前端小猎人。欢迎大家再次来到前端丛林,在这里你将会遇到各种各样的前端猎物,我希望可以把这些前端猎物统统拿下,嚼碎了服用,并成为自己身上的骨肉。今天是我们冒险的第十五天,昨天给大家介绍了一下Redux的基本应用,今天我们来介绍一下Redux的中间件,看看Redux中间件到底有哪些作用,让我们探寻其中的奥秘。话不多说,开始我们的冒险之旅吧!
1. Redux中间件
2. 实现logger中间件
中间件就是一个函数,对store.dispatch方法进行了改造,在发出 Action 和执行 Reducer 这两步之间,添加了其他功能
2.1 store\index.js
src\store\index.js
import { createStore, applyMiddleware } from '../redux';
import reducer from './reducers';
let logger = store => dispatch => action => {
console.log(store.getState().number);
dispatch(action);
console.log(store.getState().number)
};
export default applyMiddleware(logger)(createStore)(reducer);
2.2 applyMiddleware.js
src\redux\applyMiddleware.js
import compose from './compose'
export default function applyMiddleware(...middlewares) {
return createStore => (...args) => {
const store = createStore(...args);
let dispatch = () => {
throw new Error('不允许派发正在构建中的中间件!');
}
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
const chain = middlewares.map(middleware => middleware(middlewareAPI));
dispatch = compose(...chain)(store.dispatch);
return {
...store,
dispatch
}
};
}
2.3 compose.js
src\redux\compose.js
function add1(str) {
return '1' + str;
}
function add2(str) {
return '2' + str;
}
function add3(str) {
return '3' + str;
}
function compose(...funcs) {
return funcs.reduce((a, b) => (...args) => a(b(...args)));
}
let result = compose(add3, add2, add1)('xiaoming');
console.log(result);
export default function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
3. 级联中间件
3.1 Counter.js
src\components\Counter.js
import React, { Component } from 'react';
import actions from '../store/actions/counter';
import { connect } from '../react-redux'
class Counter extends Component {
render() {
return (
<div>
<p>{this.props.number}</p>
<button onClick={this.props.increment}>+</button>
<button onClick={this.props.incrementAsync}>异步+1</button>
<button onClick={this.props.incrementPromise}>promise异步+1</button>
</div>
)
}
}
let mapStateToProps = state => state;
export default connect(
mapStateToProps,
actions
)(Counter)
3.2 store\index.js
src\store\index.js
import { createStore, applyMiddleware } from '../redux';
import reducer from './reducers';
import logger from '../redux-logger';
import thunk from '../redux-thunk';
import promise from '../redux-promise';
export default applyMiddleware(thunk, promise, logger)(createStore)(reducer);
3.3 reducers\index.js
src\store\reducers\index.js
import counter from './counter';
export default counter;
3.4 actions\counter.js
src\store\actions\counter.js
import * as types from '../action-types';
export default {
increment() {
return { type: types.INCREMENT };
},
decrement() {
return { type: types.DECREMENT };
},
incrementAsync() {
return function (dispatch) {
setTimeout(() => {
dispatch({ type: types.INCREMENT });
}, 1000);
}
},
incrementPromise() {
return {
type: types.INCREMENT,
payload: new Promise((resolve, reject) => {
let result = Math.random();
if (result > .5) {
resolve(result);
} else {
reject(result);
}
}, 1000)
}
}
}
3.5 redux-logger.js
src\redux-logger.js
export default store => dispatch => action => {
console.log(store.getState().number);
dispatch(action);
console.log(store.getState().number)
};
3.6 redux-thunk.js
src\redux-thunk.js
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
if (typeof action == 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
}
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
3.7 redux-promise.js
src\redux-promise.js
function isPromise(obj) {
return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof ob
j.then === 'function';
}
export default function promiseMiddleware({ dispatch }) {
return next => action => {
return isPromise(action.payload)
? action.payload
.then(result => dispatch({ ...action, payload: result }))
.catch(error => {
dispatch({ ...action, payload: error, error: true });
return Promise.reject(error);
})
: next(action);
};
}
4. redux-persist
4.1 src\index.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import Counter from './components/Counter';
import { Provider } from './react-redux';
import { store, persistor } from './store';
import { PersistGate } from './redux-persist/integration/react'
ReactDOM.render(<Provider store={store}>
<PersistGate persistor={persistor}>
<Counter />
</PersistGate>
</Provider>, document.getElementById('root'));
4.2 store\index.js
src\store\index.js
import { createStore } from '../redux';
import reducer from './reducers';
import { applyMiddleware } from '../redux';
import logger from '../redux-logger';
import thunk from '../redux-thunk';
import promise from '../redux-promise';
import { persistStore, persistReducer } from '../redux-persist'
import storage from '../redux-persist/lib/storage'
const persistConfig = {
key: 'root',
storage,
}
const persistedReducer = persistReducer(persistConfig, reducer);
const store = applyMiddleware(thunk, promise, logger)(createStore)(persistedReducer);
let persistor = persistStore(store)
export { persistor, store };
4.3 redux-persist\index.js
src\redux-persist\index.js
import persistReducer from './persistReducer';
import persistStore from './persistStore';
export {
persistReducer,
persistStore
}
4.4 persistReducer.js
src\redux-persist\persistReducer.js
export default function (persistConfig, reducer) {
let isInited = false;
return (state, action) => {
switch (action.type) {
case 'PERSIST_INIT':
isInited = true;
let value = persistConfig.storage.getItem('persist:' + persistConfig.key);
state = value ? JSON.parse(value) : undefined;
return reducer(state, action);
default:
if (isInited) {
state = reducer(state, action);
persistConfig.storage.setItem('persist:' + persistConfig.key, JSON.stringify(state));
return state;
}
return reducer(state, action);
}
}
}
4.5 persistStore.js
src\redux-persist\persistStore.js
export default function (store) {
let persistor = {
...store,
initState() {
persistor.dispatch({
type: 'PERSIST_INIT',
})
}
};
return persistor;
}
4.6 storage.js
src\redux-persist\lib\storage.js
let storage = {
setItem(key, val) {
localStorage.setItem(key, val);
},
getItem(key) {
return localStorage.getItem(key);
}
}
export default storage;
4.7 react.js
src\redux-persist\integration\react.js
import React, { Component } from 'react';
class PersistGate extends Component {
componentDidMount() {
this.props.persistor.initState();
}
render() {
return this.props.children;
}
}
export { PersistGate }
5. redux-actions
redux-actions是一个实用的库,让编写redux状态管理变得简单起来。redux-action产生的动作是FSA标准的
5.1 单个action
5.1.1 actions\counter.js
src\store\actions\counter.js
import * as types from '../action-types';
//import { createAction } from 'redux-actions';
function createAction(type, payloadCreator) {
return function actionCreator(...args) {
return { type, payload: payloadCreator(...args) };
}
}
const add = createAction(types.ADD, (payload) => payload * 2);
const minus = createAction(types.MINUS, (payload) => payload * 2);
export default {
add,
minus
}
5.1.2 reducers\counter.js
src\store\reducers\counter.js
import * as types from '../action-types';
//import {handleAction} from 'redux-actions';
import actions from '../actions/counter';
function handleAction(type, reducer, defaultState) {
return function (state = defaultState, action) {
if (action.type === type) {
return reducer(state, action);
}
return state;
}
}
const initialState = { number: 0 };
const reducer = handleAction(types.ADD, (state, action) => {
return {
...state, number: state.number + action.payload
}
}, initialState);
export default reducer;
5.2 多个action
5.2.1 actions\counter.js
actions\counter.js
import * as types from '../action-types';
//import { createAction,createActions } from 'redux-actions';
export default createActions({
[types.ADD]: (payload) => payload * 2,
[types.MINUS]: (payload) => payload * 2
});
function createActions(actions) {
let newActions = {};
for (let type in actions) {
newActions[type] = function (...args) {
return { type, payload: actions[type](...args) }
}
}
return newActions;
}
5.2.2 reducers\counter.js
reducers\counter.js
import * as types from '../action-types';
//import {handleAction,handleActions } from 'redux-actions';
import actions from '../actions/counter';
const initialState = { number: 0 };
function handleActions(reducers, initialState) {
return function (state = initialState, action) {
let types = Object.keys(reducers);
for (let i = 0; i < types.length; i++) {
let type = types[i];
if (type === action.type) {
return reducers[type](state, action);
}
}
return state;
}
}
export default handleActions({
[types.ADD]: (state, action) => {
return {
...state, number: state.number + action.payload
}
},
[types.MINUS]: (state, action) => {
return {
...state, number: state.number - action.payload
}
}
}, initialState);
结尾
好啦,这期的前端丛林大冒险先到这里啦!这期我们了解了Redux中间件的基本使用。这期内容不太容易理解,大家一定要多读几遍,要好好啃下来嚼烂嚼透。希望大家可以好好品尝并消化,迅速升级,接下来我们才更好地过五关斩六将!好啦,我们下期再见。拜拜!