【前端丛林】React这样服用,效果更佳(16)

126 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第16天,点击查看活动详情

前言

哈喽大家好,我是Lotzinfly,一位前端小猎人。欢迎大家再次来到前端丛林,在这里你将会遇到各种各样的前端猎物,我希望可以把这些前端猎物统统拿下,嚼碎了服用,并成为自己身上的骨肉。今天是我们冒险的第十五天,昨天给大家介绍了一下Redux的基本应用,今天我们来介绍一下Redux的中间件,看看Redux中间件到底有哪些作用,让我们探寻其中的奥秘。话不多说,开始我们的冒险之旅吧!

1. Redux中间件

image.png

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. 级联中间件

image.png

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中间件的基本使用。这期内容不太容易理解,大家一定要多读几遍,要好好啃下来嚼烂嚼透。希望大家可以好好品尝并消化,迅速升级,接下来我们才更好地过五关斩六将!好啦,我们下期再见。拜拜!