paint-brush
RTK for the useReducer Hook: A Guideby@anatolii
569 reads
569 reads

RTK for the useReducer Hook: A Guide

by Anatolii KabanovNovember 23rd, 2022
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Hook is a way to keep and manage data in the store for the particular component and its children. Hook will return the State and functions to change the state. Hook then will have a standard switch-case implementation; I wrapped it with `produce` function from the Immer lib. It’s to avoid mistakes while coping in full state. But yeah, in the very original reducer where the object of the state is without multiple nested objects, I will only use the spread operator.

People Mentioned

Mention Thumbnail

Companies Mentioned

Mention Thumbnail
Mention Thumbnail
featured image - RTK for the useReducer Hook: A Guide
Anatolii Kabanov HackerNoon profile picture

When, inside your application, you already use Redux state management for centralized access to the data and for some slices of the data, there is no need to store it in the common store. For example, the case with the form, when it is handled in one determined component.


It will be unuseful to store the data in a global state and change the data only from one piece of application that is already logically combined, I mean the form component and its children. In this scenario, useReducer hook will look pretty in its place.


The useReducer hook is a way to keep and manage data in the store for the particular component and its children. So, in the basic scenario when there are no needs in the redux, I will only use the hook itself without heavy libraries just for the small feature.


const [type, dispatch] = useReducer(reducer, initializer(editedType));


For each action, it will require writing the action type and the payload. It grabs quite a lot of lines of code in the file just for the action annotation and their invocation.


interface ChangeNameAction {
    type: 'CHANGE_NAME';
    name: string;
}


To keep all reducer staff in one place, I will create another hook and return back only functions that will dispatch required actions in it to change the state. And hook will return the State and functions to change the state. Then the function to change the name:


const setName = (name: string) => {
    dispatch({ type: 'CHANGE_NAME', name });
};


The reducer function then will have a standard switch-case implementation; I wrapped it with produce function from the Immer lib. It’s to avoid mistakes while coping in full state. But yeah, in the very original reducer where the object of the state is without multiple nested objects, I will only use the spread operator.


const reducer: Reducer<TypeDraft, TypeEditorAction> = produce(
    (draft: TypeDraft, action: CustomTypeEditorAction) => {
        switch (action.type) {
            case 'CHANGE_NAME':
                draft.name = action.name;
                break;
            ...
            default:
        }
    }
);


And the new hook will be like this:


export function useTypeEditor(editedType?: EditedType) {
    const [type, dispatch] = useReducer(reducer, initializer(editedType));

    const setName = (name: string) => {
        dispatch({ type: 'CHANGE_NAME', name });
    };

    return {
        type,
        setName
    };
}


Since the reducer is just a pure function, instead of creating a reducer manually and wrapping it in the Immer produce function, it’s much more convenient to use RTK createReducer or createSlice function for the useReducer hook.


const reducer = createReducer(initialState, builder =>
    builder
        .addCase(changeNameAction, (state, { payload }) => {
            state.name = payload;
        })
);


The actions also create from createAction function instead of describing it through an interface.


const name = 'typeForm';
const changeNameAction = createAction<string>(`${name}/changeName`);


It will be the same reducer as from the result of produce function or just pure reducer function but in a more decent way. I don’t require to keep in mind how to properly handle the action. And at the end, for the component, it will be another hook you can simply use.


const TypeEditorForm: React.FC = () => {

  const editor = useTypeEditor(type);
  ...
  return (<>...</>)
}


By the way, type and initializer is here to properly set the initial state, e.g., if the type was already created, it needs to init the state from it.


Photo by Steve Johnson on Unsplash