Redux and useReducer

A quick run through

Current architecture

  • Redux + toolkit (or something like that)

  • middleware (choose your flavour)
  • action builder (maybe)
  • ...

Positive sides

  • well known technology/stack
  • scalability
  • proven dependability
  • community
  • documentation

What should I put into my store?

Negative sides

  • inherited file structure
  • "hard" to maintain/change something
  • easy to over use
  • black-box feeling (sometimes)

The proposal

  • Redux + toolkit (yeah, we still like it...)
  • axios
  • useReducer

How to use

  • Use Redux only when it actually make sense like (actual) global state or highly dynamic state that's used horizontally or by multiple components.
  • use Axios as middleware for AJAX using the cancelable promise token and Promisse.All (when necessary).
  • use useReducer for component-level (vertical) shared state.

Structure example

App-level state (example: session info)

component

component

component

component

component

component

component-level state

component-level state

export const componentReducer = (
  state: ComponentState = initialState,
  action: ActionsType
): ComponentState => {
  switch (action.type) {
    case 'TYPE-1':
      return {
        ...state,
        [some_piece_of_state]: action.payload,
      };

    // ...

    default:
      return state;
  }
};

The Reducer

const CancelToken = axios.CancelToken;
const source = CancelToken.source();
const axiosInstance = axios.create({
  baseURL: 'https://url.com',
  headers: ...,
  cancelToken: source.token,
});

const MyComponent: FC<Props> = props => {
	const [state, dispatch] = useReducer(componentReducer, initialState);
    
    const fetchStuff = (data) => {
    	axiosInstance.post('some_url', data)
        	.then(...)
    }
    
    const handleCancelFetch = () => {
    	source.cancel();
    }
    
    const handleSomeDispatch = data => {
    	dipatch(({ type: 'TYPE_!', payload: data }));
    }
    
    useEffect(() => {
      return () => {
      	// some logic here...

          source.cancel();
      };
    }, [your_dependencies]);
    
    return (
    	...
    );
};

import { useEffect, useCallback, useRef } from 'react';
import axios from 'axios';

const CancelToken = axios.CancelToken;
const source = CancelToken.source();
const axiosInstance = axios.create({
  baseURL: 'https://url.com',
  headers: ...,
  cancelToken: source.token,
});

export const usePostData = <T>(url: string, data: <your_type_here>) => {
  const promise = useRef<Promise<T>>();

  const handler = useCallback(() => {
	promise.current = axiosInstance.post(url, data);
    
    return promise.current
  },
    [url, data]
  );

  useEffect(() => {
    // some logic
    if (promise.current) source.cancel();    
  }, []);

  return handler;
};

Custom hook example

export type TReducerDict = { [k: string]:  Reducer};

export const combineReducers = (slices:  ReducerDict) => (state: AppState, action: Action) =>
  Object.keys(slices).reduce(
    (acc, prop) => ({
      ...acc,
      [prop]: slices[prop](acc, action)[prop],
    }),
    state
  );

Combine reducers example

Redux and usereducer​

By Thiago Dallacqua

Redux and usereducer​

  • 17