blog_robinwieruch_content icon indicating copy to clipboard operation
blog_robinwieruch_content copied to clipboard

No next method or return value in the useReducer + middleware example

Open dht opened this issue 3 years ago • 1 comments

In the this example, currently, the middleware does not take in account two important aspects of middlewares:

  1. the next method
  2. the return value mechanism

The next method enables you to break the middleware chain in certain circumstances. The return value mechanism allows you to bubble up promises or return values to the entity which invoked the dispatch method.

dht avatar May 03 '22 12:05 dht

Perhaps something like that:

import { Reducer, useMemo, useReducer } from 'react';

type Json = Record<string, any>;
type Action = { type: string } & Json;
type Middleware = (store: any) => (next: any) => (action: Action) => any;

export function useReducerWithMiddleware<T>(
    reducer: Reducer<any, any>,
    initialState: T,
    middlewares: Middleware[]
) {
    const [state, dispatch] = useReducer(reducer, initialState);

    const mockStore = useMemo(
        () => ({
            dispatch, //
            getState: () => state,
            updateReducer: () => {},
        }),
        [state, dispatch]
    );

    const dispatchWithMiddleware = (action: Action) => {
        let output;

        for (let middleware of middlewares) {
            let nextWasCalled = false;

            const next = () => {
                nextWasCalled = true;
            };

            output = middleware(mockStore)(next)(action) || output;

            if (!nextWasCalled) {
                break;
            }
        }

        dispatch(action);
    };

    return [state as T, dispatchWithMiddleware];
}

Which will make it possible to use the same middlewares redux signature:

const logger = (store: any) => (next: any) => (action: any) => {
    console.log('dispatching', action);
    let result = next(action);
    console.log('next state', store.getState());
    return result;
};

export const Component = (props) => {
    const [state, dispatch] = useReducerWithMiddleware(
       reducer,
        initialState,
        [logger]
    );
//  ...component definition...
}

dht avatar May 03 '22 13:05 dht