Redux 与 Redux-Toolkit 的使用

1,604 阅读13分钟

redux

介绍

Redux 是 JavaScript 状态容器,提供可预测化的状态管理

redux 是一个状态管理工具,通常与 redux 一起使用,但是 redux 可以在任何 js 代码中使用,并不是 react 的专属工具

redux APIs

image-20220503163512458

Redux 的 api 一共有五个函数,其中createStore是主要的创建 store 的函数,其他四个都是辅助函数。

  1. compose

    从右到左来组合多个函数。这是函数式编程中的方法,为了方便,被放到了 Redux 里。

    const makeLouder = (str: string) => str.toUpperCase();
    const repeatThreeTimes = (str: string) => str.repeat(3);
    const embolden = (str: string) => str.bold();
    
    const makeLouderRepeatThreeTimesEmbolden = (str: string) => embolden(repeatThreeTimes(makeLouder(str)));
    
    // 使用 compose,最终效果同上
    const makeLouderRepeatThreeTimesEmbolden = compose(embolden, repeatThreeTimes, makeLouder);
    
    console.log('log', makeLouderRepeatThreeTimesEmbolden('hello'));
    
  2. createStore

    创建一个 Redux store 来以存放应用中所有的 state。应用中应有且仅有一个 store。

    参数

    1. reducer (Function): 接收两个参数,分别是当前的 state 树和要处理的 action,返回新的 state 树。reducer 应该是一个纯函数,参数进入,函数处理,返回值。action 应该是一个对象,要求有一个类型 type 字段,可以是任何有效的字符串大小写都可以,告诉 redux 此次进入的类型
    2. preloadedState (any): 默认值,store 的初始值,可以在第二个参数中传递,也可以直接写在 reducer 的默认参数
    3. enhancer (Function): Store enhancer 是一个组合 store creator 的高阶函数,返回一个新的强化过的 store creator。这与 middleware 相似,它也允许你通过复合函数改变 store 接口。

    返回值

    Store: 保存了应用所有 state 的对象。改变 state 的惟一方法是 dispatch action。你也可以 subscribe 监听 state 的变化,然后更新 UI。

    const INCREMENT = 'INCREMENT';
    const ADD = 'ADD';
    
    const increment = () => ({ type: INCREMENT });
    const add = (data) => ({ type: ADD, payload: data });
    
    const initalState = { value: 0 };
    const incrementAction = { type: 'INCREMENT', payload: 1 };
    
    const reducer = (state = initalState, action) => {
        switch (action.type) {
            case INCREMENT:
                return { value: state.value + 1 };
            case ADD:
                return { value: state.value + action.payload };
            default:
                break;
        }
        return state;
    };
    
    export const store = createStore(reducer);
    
    const subscriber = () => console.log('SUBSCRIBER', store.getState());
    
  3. bindActionCreators

    把一个 value 为不同 action creator 的对象,转成拥有同名 key 的对象。同时使用 dispatch方法对每个 action creator 进行包装,以便可以直接调用它们。

    参数

    1. actionCreators (Function or Object): 一个 action creator,或者一个 value 是 action creator 的对象。
    2. dispatch (Function): 一个由 Store 实例提供的 dispatch 函数。

    返回值

    (Function or Object): 一个与原对象类似的对象,只不过这个对象的 value 都是会直接 dispatch 原 action creator 返回的结果的函数。如果传入一个单独的函数作为 actionCreators,那么返回的结果也是一个单独的函数。

    // store.dispatch(increment());
    // store.dispatch(add(1000));
    
    // 使用 bindActionCreators 方法后,可以直接调用,不需要每次都使用 dispatch
    const actions = bindActionCreators({ increment, add }, store.dispatch);
    
    actions.add(1000);
    actions.increment();
    
  4. combineReducers

    随着应用变得越来越复杂,可以考虑将 reducer 函数 拆分成多个单独的函数,拆分后的每个函数负责独立管理 state 的一部分。

    combineReducers 辅助函数的作用是,把一个由多个不同 reducer 函数作为 value 的 object,合并成一个最终的 reducer 函数,然后就可以对这个 reducer 调用 createStore 方法。

    合并后的 reducer 可以调用各个子 reducer,并把它们返回的结果合并成一个 state 对象。 combineReducers() 返回的 state 对象,会将传入的每个 reducer 返回的 state 按其传递给 combineReducers() 时对应的 key 进行命名

    import { createStore, combineReducers } from 'redux';
    
    const initialState = {
        users: [
            { id: 1, name: 'aaa' },
            { id: 2, name: 'bbb' }
        ],
        tasks: [{ title: 'go home' }, { title: 'open VSCode' }]
    };
    
    const ADD_USER = 'ADD_USER';
    const ADD_TASK = 'ADD_TASK';
    
    const addUser = (name) => ({ type: ADD_USER, payload: { name } });
    const addTask = (title) => ({ type: ADD_TASK, payload: { title } });
    
    const userReducer = (user = initialState.users, action) => {
        if (action.type === ADD_USER) {
            return [...user, action.payload];
        }
    
        return user;
    };
    
    const taskReducer = (task = initialState.tasks, action) => {
        if (action.type === ADD_TASK) {
            return [...task, action.payload];
        }
    
        return task;
    };
    
    const reducer = combineReducers({ users: userReducer, tasks: taskReducer });
    
    export const store = createStore(reducer);
    
  5. applyMiddleware

    使用包含自定义功能的 middleware 来扩展 Redux 是一种推荐的方式。Middleware 可以让你包装 store 的 dispatch 方法来达到你想要的目的。同时, middleware 还拥有“可组合”这一关键特性。多个 middleware 可以被组合到一起使用,形成 middleware 链。其中,每个 middleware 都不需要关心链中它前后的 middleware 的任何信息。

    学习中间件,就要先了解增强器enhancer

    const monitorEnhancer = (createStore) => (reducer, initialState, enhancer) => {
        const monitoreReducer = (state, action) => {
            const start = performance.now();
            const newState = reducer(state, action);
            const end = performance.now();
            console.log(end, start);
    
            return newState;
        };
    
        return createStore(monitoreReducer, initialState, enhancer);
    };
    
    const logEnhancer = (createStore) => (reducer, initialState, enhancer) => {
        const logReducer = (state, action) => {
            console.log('🚀 ~ this is old state', state, action);
    
            const newState = reducer(state, action);
    
            console.log('🚀 ~ this is newState', newState);
    
            return newState;
        };
    
        return createStore(logReducer, initialState, enhancer);
    };
    
    // export const store = createStore(reducer, monitorEnhancer);
    // export const store = createStore(reducer, logEnhancer);
    export const store = createStore(reducer, compose(logEnhancer, monitorEnhancer));
    

    增强器例子如上所示,可能整个工作学习中都不会使用到,有了解认知就好。

    Middleware 最常见的使用场景是无需引用大量代码或依赖类似 Rx 的第三方库实现异步 actions。这种方式可以让你像 dispatch 一般的 actions 那样 dispatch 异步 actions

    const logMiddleware = (store) => (next) => (action) => {
        console.log('old state', store.getState(), action);
        next(action);
        console.log('new state', store.getState(), action);
    };
    
    const monitorMiddleware = (store) => (next) => (action) => {
        const start = performance.now();
        next(action);
        const end = performance.now();
        console.log(end, start);
    };
    
    export const store = createStore(reducer, applyMiddleware(logMiddleware, monitorMiddleware));
    

    使用 react-redux

    首先创建 actions.js 文件,文件中写入

    export const INCREMENT = 'INCREMENT';
    export const DECREMENT = 'DECREMENT';
    export const SET = 'SET';
    
    export const increment = () => ({ type: INCREMENT });
    export const decrement = () => ({ type: DECREMENT });
    export const set = (payload) => ({ type: SET, payload });
    

    接着创建 reducer.js 文件,引入 actions.js 文件中的值

    import { INCREMENT } from './actions';
    
    export const initialState = { count: 0 };
    
    export const reducer = (state = initialState, action) => {
        if (action.type === INCREMENT) {
            return { count: state.count + 1 };
        }
    
        if (action.type === DECREMENT) {
            return { count: state.count - 1 };
        }
    
        if (action.type === SET) {
            return { count: action.payload };
        }
    
        return state;
    };
    

    创建 store.js 文件,引入reducer.js 文件中的值

    import { createStore } from 'redux';
    import { reducer } from './reducer';
    
    export const store = createStore(reducer);
    

    接着在文件的入口文件或者在需要使用 redux 中的值的组件的最上级使用 import { Provider } from 'react-redux';引入Provider组件,引入 store,包裹住需要的组件

    <Provider store={store}>
        <App />
    </Provider>
    

    现在就创建了一个基本的 redux 使用实现,我们可以继续添加一个工具 Redux DevTools

    使用 Redux DevTools可以很方便的看见数据在 redux 中的流动,首先需要在浏览器中安装Redux DevTools插件,能访问 chrome 插件市场的可以直接下载,不能的话百度直接搜也可以找到

    1. export const store = createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());直接在 createStore 中使用window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()即可

    2. import { createStore } from 'redux';
      import { reducer } from './reducer';
      import { composeWithDevTools } from 'redux-devtools-extension';
      
      export const store = createStore(reducer, composeWithDevTools());
      

      引入redux-devtools-extension直接使用即可

    在组件中使用 redux

    在组件中使用 redux 有两种方式,一种是使用 hooks 的方式,还有一种是使用 connect方式

    1. hooks

      // 首先需要引入 actions 还有 useDispatch, useSelector
      import { useDispatch, useSelector } from 'react-redux';
      import { decrement, increment, set } from './redux';
      import { bindActionCreators } from 'redux';
      
      // 需要哪些值就直接在 useSelector 中导出,并且要引入 dispatch
      // useSelector 中可以传第二个对比函数,如果不传就是默认的判断是否相等函数
      const count = useSelector((state) => state.count);
      const dispatch = useDispatch();
      
      // 在需要的地方直接如下写法即可
      dispatch(increment());
      dispatch(decrement());
      dispatch(set(0));
      
      // 如果觉得每次都要写 dispatch 有些麻烦的话,可以使用 bindActionCreators
      const actions = bindActionCreators({ increment, decrement, set }, dispatch);
      actions.increment();
      
      // 还可以将整个 hooks 提取,封装成自定义 hook
      
    2. connect 函数

      // mapStateToProps
      const mapStateToProps = (state) => ({
          count: state.count
      });
      
      // mapDispatchToProps 有两种方式,可以直接返回一个对象,其中每一项是会被自动 dispatch,另一种是手动 dispatch 对应的函数
      // const mapDispatchToProps = {
      //     increment,
      //     decrement,
      //     set,
      // };
      
      const mapDispatchToProps = (dispatch: Dispatch, ownProps) => ({
          increment: () => dispatch(increment()),
          decrement: () => dispatch(decrement()),
          set: (num: number) => dispatch(set(num))
      });
      
      export default connect(mapStateToProps, mapDispatchToProps)(App);
      

    reselect

    继续学习之前,可以先了解一下 reselect,reselect 是一个 Redux 的选择器库。

    • Selector 可以计算衍生的数据,可以让 Redux 存储尽可能少的 state 。
    • Selector 非常高效,除非某个参数发生变化,否则不会发生计算过程。
    • Selector 是可组合的,它们可以输入、传递到其他的选择器。
    import { createSelector } from 'reselect';
    
    const shopItemsSelector = (state) => state.shop.items;
    const taxPercentSelector = (state) => state.shop.taxPercent;
    
    const subtotalSelector = createSelector(shopItemsSelector, (items) =>
        items.reduce((acc, item) => acc + item.value, 0)
    );
    
    const taxSelector = createSelector(
        subtotalSelector,
        taxPercentSelector,
        (subtotal, taxPercent) => subtotal * (taxPercent / 100)
    );
    
    export const totalSelector = createSelector(subtotalSelector, taxSelector, (subtotal, tax) => ({
        total: subtotal + tax
    }));
    
    // 使用的时候,可以直接放到 mapStateToProps 中
    const mapStateToProps = (state) => ({
        total: totalSelector(state)
    });
    

Redux Toolkit

Redux 被广泛应用于 React 应用中,但是 redux 也有以下问题:

  • 配置 Redux Store 并不简单;
  • 通常需要引入另外几个库来使 Redux 与 React 一起工作;
  • Redux 需要太多样板代码。

Redux Toolkit 解决的问题

Redux Toolkit 提供了基于 redux 的封装,简化了 redux 创建流程及样板代码量,让我们能更加关注状态管理,同时 Redux Toolkit 附带了一些有用的工具库,例如ImmerRedux-ThunkReselect等,并且无需在手动引入 redux-devtools-extension

API

ConfigureStore

ConfigureStore 用来代替原生的 createStore 方法,调用 createStore() 来创建一个 Redux store ,并传入你的根 reducer 函数。

该方法会默认包含中间件,如 thunk 如果你想添加自定义中间件,可以参考如下写法,使用getDefaultMiddleware 方法来获取默认的中间件,然后添加上自己的中间件,如果你不想使用默认中间件,可以直接传一个中间件数组

// 之前:
const store = createStore(reducer);

// 之后:
const store = configureStore({
    reducer: reducer
});

const rootReducer = combineReducers({ count: counterSlice.reducer, todo: todosReducer });

// 添加自定义中间件
// 如果你不想使用默认中间件,可以直接传递数组,例如 middleware: [thunk, logger]
const store = configureStore({
    reducer: rootReducer,
    middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(routerMiddleware(history)),
    enhancers: [sentryReduxEnhancer]
});

createAction

createAction 接受一个 action 类型字符串作为参数,并返回一个使用该类型字符串的 action creator 函数。

// 原生实现
const ADD = 'ADD';

function addType() {
    return { type: ADD };
}

console.log(addType());
// {type: "ADD"}

// 或者,使用 `createAction` 来生成 action creator:
const addAction = createAction('addType');

console.log(addAction());
// {type: "ADD"}

当我们需要引用新创建的 action type 的时候,可以直接使用 toString 方法,或者使用 .type 字段,都可以返回 action type 字符串,之后的使用,就和写 Redux 样板创建的 action type 用法相同

const add = createAction('ADD');

console.log(add.toString());
// "ADD"

console.log(add.type);
// "ADD"

createAsyncThunk

类似于上面 createAction,但是是创建一个异步的 action

type AsyncThunkConfig = {
    /** return type for `thunkApi.getState` */
    state?: unknown;
    /** type for `thunkApi.dispatch` */
    dispatch?: Dispatch;
    /** type of the `extra` argument for the thunk middleware, which will be passed in as `thunkApi.extra` */
    extra?: unknown;
    /** type to be passed into `rejectWithValue`'s first argument that will end up on `rejectedAction.payload` */
    rejectValue?: unknown;
    /** return type of the `serializeError` option callback */
    serializedErrorType?: unknown;
    /** type to be returned from the `getPendingMeta` option callback & merged into `pendingAction.meta` */
    pendingMeta?: unknown;
    /** type to be passed into the second argument of `fulfillWithValue` to finally be merged into `fulfilledAction.meta` */
    fulfilledMeta?: unknown;
    /** type to be passed into the second argument of `rejectWithValue` to finally be merged into `rejectedAction.meta` */
    rejectedMeta?: unknown;
};

export const ic = createAsyncThunk('user/name', async (name: string, thunkApi: AsyncThunkConfig) => {
    const res = await fetch(api).then((response) => response.json());
    return res;
});

store
    .dispatch(ic('123'))
    .unwrap()
    .then((originalPromiseResult) => {
        // handle result here
    })
    .catch((rejectedValueOrSerializedError) => {
        // handle error here
    });

// unwrapResult 方法的使用
import { unwrapResult } from '@reduxjs/toolkit';

const resultAction = await dispatch(ic('123'));
const originalPromiseResult = unwrapResult(resultAction);

异步的 createAsyncThunk在被 dispatch之后,此 store.dispatch(ic('123'))会返回一个 promise,可以使用 await 或者 then 方法,如果直接调用 then方法,可以拿到此次 dispatch的全部信息,包括 @reduxjs/toolkit的内部信息还有结果信息;此时可以如上最下方示例,直接调用unwrap()方法,会过滤掉除结果信息外的多余信息,推荐直接使用unwrap(),也导出了 unwrapResult可以处理返回值,

createReducer

createReducer 最基础的用法类似于样板代码创建 reducer。为 reducer 函数接受一个初始状态值和 action type 的查找表,并创建一个 reducer 来处理所有这些 action type。

  • addCase

    创建一个新的 action 处理函数,必须写在最上面,参数

    • type 类型,与原生 redux 的 type 一样,可以使用 createAction createAsyncThunk string
    • reducer 类似原生的 reducer
  • addMatcher

    允许你传入的操作与你自己的过滤器函数匹配,而不仅仅是action.type属性。

    如果多个 matcher reducer 匹配,所有这些都将按照它们定义的顺序执行 - 即使 case reducer 已经匹配。所有的builder.addMatcher必须在对builder.addCase之后和对builder.addDefaultCase之前。

  • addDefaultCase

    添加一个默认的 action 处理函数,如果没有为此操作执行 case reducer 和 matcher reducer,就会执行此 reducer

import { AnyAction, AsyncThunk, createAction, createAsyncThunk, createReducer } from '@reduxjs/toolkit';
// 例子来自 https://redux-toolkit-cn.netlify.app/api/createReducer
type GenericAsyncThunk = AsyncThunk<unknown, unknown, any>;

type PendingAction = ReturnType<GenericAsyncThunk['pending']>;
type RejectedAction = ReturnType<GenericAsyncThunk['rejected']>;
type FulfilledAction = ReturnType<GenericAsyncThunk['fulfilled']>;

function isPendingAction(action: AnyAction): action is PendingAction {
    return action.type.endsWith('/pending');
}

interface CounterState {
    value: number;
    loading: string;
    [key: string]: string | number;
}

// 创建 action
const increment = createAction('counter/increment');
const decrement = createAction('counter/decrement');
const incrementByAmount = createAction<number>('counter/incrementByAmount');

// createReducer,创建的 reducer 也引入在创建 store 处即可
export const todosReducer = createReducer(initialState, (builder) => {
    // 最基础的用法,使用 createAction 创建 action,在此处引用
    builder
        .addCase(increment, (state, action) => {
            state.value++;
        })
        .addCase(decrement, (state, action) => {
            state.value--;
        })
        .addCase(incrementByAmount, (state, action) => {
            state.value += action.payload;
        })
        // 此处 ic 引用上面 createAsyncThunk 创建的 ic,异步的 action 有三种状态,可以分别监听
        // 开始异步 action 时会触发 pedding 状态
        .addCase(ic.pending, (state, action) => {
            state.loading = 'pedding';
        })
        // 异步 action 完成时会触发 fulfilled 状态
        .addCase(ic.fulfilled, (state, action) => {
            state.loading = 'fulfilled';
        })
        // 异步 action 失败时会触发 rejected 状态
        .addCase(ic.rejected, (state, action) => {
            state.loading = 'rejected';
        })
        .addMatcher(isPendingAction, (state, action) => {
            state[action.meta.requestId] = 'pending';
        })
        .addMatcher(
            // matcher can be defined inline as a type predicate function
            (action): action is RejectedAction => action.type.endsWith('/rejected'),
            (state, action) => {
                state[action.meta.requestId] = 'rejected';
            }
        )
        // matcher can just return boolean and the matcher can receive a generic argument
        .addMatcher<FulfilledAction>(
            (action) => action.type.endsWith('/fulfilled'),
            (state, action) => {
                state[action.meta.requestId] = 'fulfilled';
            }
        )
        // 添加一个默认的
        .addDefaultCase((state, action) => {
            state.otherActions++;
        });
});

// createReducer 的第二个参数,还可以传递一个对象,对象的 key 是 action 的 type,值就是一个 reducer
export const todosReducer = createReducer(initialState, {
    [increment.type]: (state, action) => {
        state.value++;
    },
    [ic.pending.type]: (state, action) => {
        state.loading = 'pedding';
    }
});

createSlice

接受一个初始状态和一个包含 reducer 名称和函数的查找表,并自动生成 action creator 函数、action type 字符串和一个 reducer 函数

// 最基础的 slice,只需传入 name、initialState、reducers,显著减少 redux 样板代码量
// ConfigureStore 时传入的 reducer 要使用 counterSlice.reducer
export const counterSlice = createSlice({
    // 唯一 name,传入字符串即可
    name: 'counter',
    // 初始默认值
    initialState: 0,
    // reducer
    reducers: {
        increment: (state: number) => state + 1,
        decrement: (state: number) => state - 1,
        set: (state: number, action: PayloadAction<number>) => action.payload
    }
});

// actions,导出 actions,使用处直接引入,使用 dispatch 函数触发即可 dispatch(actions.increment())
export const actions = counterSlice.actions;

除了上面的基础用法,createSlice 还有 extraReducers 参数,该字段定义了额外的 reducer

extraReducers 有两种参数形式,类型定义如下

此处的 extraReducers 写法,与上面的createReducer写法相同,都可以是对象或者函数,具体写法参考上面的写法

type extraReducers =
    | Object<string, ReducerFunction>
    | ((builder: ActionReducerMapBuilder<State>) => void)

export const counterSlice = createSlice({
  	...,
  	extraReducers: (builder) => {
        builder.addCase(increment, (state, action) => {
            state.value++;
        })
    },
});


createEntityAdapter

官网介绍:A function that generates a set of prebuilt reducers and selectors for performing CRUD operations on a normalized state structure containing instances of a particular type of data object. These reducer functions may be passed as case reducers to createReducer and createSlice. They may also be used as "mutating" helper functions inside of createReducer and createSlice.

个人理解:定义一个辅助函数,能够同时操作一组相似的数据,减少操作相似数据时需要多加的代码量

// 例子来自 https://redux-toolkit-cn.netlify.app/api/createEntityAdapter
import { createEntityAdapter, createSlice, configureStore } from '@reduxjs/toolkit';

// Since we don't provide `selectId`, it defaults to assuming `entity.id` is the right field
const booksAdapter = createEntityAdapter({
    // Keep the "all IDs" array sorted based on book titles
    sortComparer: (a, b) => a.title.localeCompare(b.title)
});

const booksSlice = createSlice({
    name: 'books',
    initialState: booksAdapter.getInitialState({
        loading: 'idle'
    }),
    reducers: {
        // Can pass adapter functions directly as case reducers.  Because we're passing this
        // as a value, `createSlice` will auto-generate the `bookAdded` action type / creator
        bookAdded: booksAdapter.addOne,
        booksLoading(state, action) {
            if (state.loading === 'idle') {
                state.loading = 'pending';
            }
        },
        booksReceived(state, action) {
            if (state.loading === 'pending') {
                // Or, call them as "mutating" helpers in a case reducer
                booksAdapter.setAll(state, action.payload);
                state.loading = 'idle';
            }
        },
        bookUpdated: booksAdapter.updateOne
    }
});

const { bookAdded, booksLoading, booksReceived, bookUpdated } = booksSlice.actions;

const store = configureStore({
    reducer: {
        books: booksSlice.reducer
    }
});

// Check the initial state:
console.log(store.getState().books);
// {ids: [], entities: {}, loading: 'idle' }

const booksSelectors = booksAdapter.getSelectors((state) => state.books);

store.dispatch(bookAdded({ id: 'a', title: 'First' }));
console.log(store.getState().books);
// {ids: ["a"], entities: {a: {id: "a", title: "First"}}, loading: 'idle' }

store.dispatch(bookUpdated({ id: 'a', changes: { title: 'First (altered)' } }));
store.dispatch(booksLoading());
console.log(store.getState().books);
// {ids: ["a"], entities: {a: {id: "a", title: "First (altered)"}}, loading: 'pending' }

store.dispatch(
    booksReceived([
        { id: 'b', title: 'Book 3' },
        { id: 'c', title: 'Book 2' }
    ])
);

console.log(booksSelectors.selectIds(store.getState()));
// "a" was removed due to the `setAll()` call
// Since they're sorted by title, "Book 2" comes before "Book 3"
// ["c", "b"]

console.log(booksSelectors.selectAll(store.getState()));
// All book entries in sorted order
// [{id: "c", title: "Book 2"}, {id: "b", title: "Book 3"}]

其他 Redux 库介绍

  • redux-persist

    如果有持久化的需求,可以使用 redux-persist此库

    import { persistReducer } from 'redux-persist';
    import storage from 'redux-persist/lib/storage';
    
    const persistConfig = {
        key: 'root',
        storage,
        // 需要持久化的 reducer
        whitelist: ['counter']
    };
    
    const persistedReducer = persistReducer(persistConfig, rootReducer);
    
    const store = configureStore({
        reducer: persistedReducer
    });
    
  • redux-observable

    如果你对 RxJS 或 流 的操作形式感兴趣,可以使用此库,redux-observable操作流中,可以使用 RxJS 提供的各种操作符,多种操作符会有各种各样的组合以达到目的

    import { Epic, ofType } from 'redux-observable';
    import { of } from 'rxjs';
    import { switchMap } from 'rxjs/operators';
    import { createAction } from '@reduxjs/toolkit';
    
    const unmountEA = createAction('unmount-epic');
    
    const unmountEAEpic: Epic = (action$) =>
        action$.pipe(
            ofType(unmountEA.type),
            switchMap(() => {
                return of(other.actions.setCancelAction('cancel'), actions.setAction(undefined));
            })
        );
    
    // 另一个文件中直接 dispatch 即可
    dispatch(unmountEA());
    

参考

Redux

redux-toolkit

Redux 工具包

Reselect Redux 的选择器库

使用 Redux Toolkit 简化 Redux

Redux的基础知识

全文基于个人理解,如果有错误的地方,欢迎指正!