useReducer

Syntax

useReducer(reducer, initialState): [state, dispatch]

In many cases useState and useAtom are sufficiently capable of dealing with the state of components, especially when separated into many smaller hooks with a single responsibility. However, sometimes you will have to deal with more complex components or you may want to migrate an existing melody-component to melody-hooks without having to rewrite a lot. In those situations, the useReducer hook might be the very best and most valuable tool at your disposal.

Example

const galleryReducer = (state, action) => {
    switch(action.type) {
        case 'SWIPE_RIGHT':
        case 'PREV_CLICKED':
            return {
                ...state,
                index: index - 1 < 0
                    ? images.length - 1
                    : index - 1
            };
        case 'SWIPE_LEFT':
        case 'NEXT_CLICKED':
        case 'CLICKED':
            return {
                ...state,
                index: index + 1 === images.length
                    ? 0
                    : index + 1
            };
        case 'IMAGES_CHANGED':
            return {
                ...state,
                images: action.payload,
                // make sure we are at a valid index
                index: index < action.payload.length
                    ? index
                    : action.payload.length - 1
            };
        default:
            return state;
    }
};

function useGallerySlider({ images }) {
    // get the current state and a dispatcher function
    const [state, dispatch] = useReducer(galleryReducer, { index: 0, images });
    // we could assume images never change... but we do want to have a realistic
    // example here so we'll deal with it
    useEffect(() => {
        dispatch({ type: 'IMAGES_CHANGED', images });
    }, images);
    const rootElement = useRef(null);
    // some fake utilities to setup event handlers
    useGestures(rootElement, {
        swipeLeft: () => dispatch({ type: 'SWIPE_LEFT' }),
        swipeRight: () => dispatch({ type: 'SWIPE_RIGHT' })
    });
    useEventHandler(rootElement, {
        'click': () => dispatch({ type: 'CLICKED' }),
        'click:.next': () => dispatch({ type: 'NEXT_CLICKED' }),
        'click:.prev': () => dispatch({ type: 'PREV_CLICKED' })
    });
    return {
        rootElement,
        ...state
    };
}

A word about reducers and Redux...

The concepts used for a reducer seem very simple but there are a lot of mistakes you can make when using them.

If you intend to use them on a regular basis, you should invest some time into reading Human Redux, a book about Redux by one of its main authors.

Last updated

Was this helpful?