React學習之Redux高階運用

在Redux架構中,reducer是一個純函數,它的職責是根據previousState和action計算出新的state。在複雜應用中,Redux提供的combineReducers讓咱們能夠把頂層的reducer拆分紅多個小的reducer,分別獨立地操做state樹的不一樣部分。而在一個應用中,不少小粒度的reducer每每有不少重複的邏輯,那麼對於這些reducer,如何抽取公共邏輯,減小代碼冗餘呢?這種狀況下,使用高階reducer是一種較好的解決方案redux

  1. reducer複用

咱們將頂層的reduce拆分紅多個小的reducer,確定會碰到reducer複用問題。例若有A和B兩個模塊,它們的UI部分類似,此時能夠經過配置不一樣的props來區別它們。那麼這種狀況下,A和B模塊能不能共用一個reducer呢?答案是否認的。咱們先來看一個簡單reducer:架構

const LOAD_DATA = 'LOAD_DATA';
const initialState = { ... };

function loadData() {
    return {
        type: LOAD_DATA,
        ...
    };
}

function reducer(state = initialState, action) {
    switch(action.type) {
        case LOAD_DATA:
            return {
                ...state,
                data: action.payload
            };
        default:
            return state;
    }
}

若是咱們將這個reducer綁定到A和B兩個不一樣模塊,形成的問題將會是,當A模塊調用loadData來分發相應的action時,A和B的reducer都會處理這個action,而後A和B的內容就徹底一致了。函數

這裏咱們必需意識到,在一個應用中,不一樣模塊間的actionType必須是全局惟一的。spa

所以,要解決actionType惟一的問題,還有一個方法就是經過添加前綴的方式來作到:code

function generateReducer(prefix, state) {
    const LOAD_DATA = prefix + 'LOAD_DATA';
    
    const initialState = { ...state, ...};
    
    return function reducer(state = initialState, action) {
        switch(action.type) {
            case LOAD_DATA:
                return {
                    ...state,
                    data: action.payload
                };
            default:
                return state;
        }
    }
}

這樣只要A和B模塊分別調用generateReducer來生成相應的reducer,就能解決reducer複用的問題了。而對於prefix,咱們能夠根據本身的項目結構來決定,例如${頁面名稱}_${模塊名稱}。只要可以保證全局惟一性,就能夠寫成一種前綴。it

  1. reducer加強

除了解決複用問題,高階reducer的另外一個重要做用就是對原始的reducer進行加強。redux-undo就是典型的利用高階reducer來加強reducer的例子,它主要做用是使任意reducer變成能夠執行撤銷和重作的全新reducer。咱們來看看它的核心代碼實現:io

function undoable(reducer) {
    const initialState = {
        // 記錄過去的state
        past: [],
        // 以一個空的action調用reducer來產生當前值的初始值
        present: reducer(undefined, {}),
        // 記錄後續的state
        future: []
    };
    
    return function(state = initialState, action) {
        const { past, present, future } = state;
        
        switch(action.type) {
            case '@@redux-undo/UNDO':
                const previous = past[past.length - 1];
                const newPast = past.slice(0, past.length - 1);
                
                return {
                    past: newPast,
                    present: previous,
                    future: [ present, ...future ]
                };
            case '@@redux-undo/REDO':
                const next = future[0];
                const newFuture = future.slice(1);
                
                return {
                    past: [ ...past, present ],
                    present: next,
                    future: newFuture
                };
            default:
                // 將其餘action委託給原始的reducer處理
                const newPresent = reducer(present, action);
                
                if(present === newPresent) {
                    return state;
                }
                
                return {
                    past: [ ...past, present ],
                    present: newPresent,
                    future: []
                };
        }
    };
}

有了這高階reducer,就能夠對任意一個reducer進行封裝:ast

import { createStore } from 'redux';

function todos(state = [], action) {
    switch(action.type) {
        case: 'ADD_TODO':
        // ...
    }
}

const undoableTodos = undoable(todos);
const store = createStore(undoableTodos);

store.dispatch({
    type: 'ADD_TODO',
    text: 'Use Redux'
});

store.dispatch({
    type: 'ADD_TODO',
    text: 'Implement Undo'
});

store.dispatch({
    type: '@@redux-undo/UNDO'
});

查看高階reducer undoable的實現代碼能夠發現,高階reducer主要經過下面3點來加強reducer:
可以處理額外的action;
可以維護更多的state;
將不能處理的action委託給原始reducer處理。function

相關文章
相關標籤/搜索