Context API vs. Redux in a React Application

kittyradulescu

Cristina Radulescu

Posted on April 24, 2020

Context API vs. Redux in a React Application

In this article I want to make a comparison between state in Redux and state in the Context API.

Redux

The state is maintained in a store which is connected to reducers, actions and middleware. In this example I will use saga middleware for fetching the information from the server and dispatching redux actions. The store information is then sent to the components using a Provider.

Store

export const sagaMiddleware = createSagaMiddleware();
const store = createStore(eventReducer);
sagaMiddleware.run(rootSaga);

export const EventsConnector = (props) => (
    <Provider store={store}>
        <AllEvents {...props}/>
    </Provider>
);

Reducer

export const initialState = {
    state: null,
    events: [],
};

export const eventReducer = (state = initialState, action) => {

    switch (action.type) {
        case FETCH_EVENTS:
            return {...state, state: "LOADING"};
        case FINISH_FETCHING_EVENTS:
            return {...state, events: action.data, state: "LOADED"};
            }
    return state;
};

Actions

The actions startFetchingEvents and finishFetchingEvents will be dispatched to update the state in Redux.

export const FETCH_EVENTS = "FETCH_EVENTS";
export const FINISH_FETCHING_EVENTS = "FINISH_FETCHING_EVENTS";

const createAction = (type, data) => ({ type, data });

export const startFetchingEvents = () => createAction(FETCH_EVENTS);

export const finishFetchingEvents = (events) => createAction(FINISH_FETCHING_EVENTS, events);

Setting the state

For setting the state an action startFetchingEvents is dispatched which triggers the the saga which after fetching the events from the server will dispatch finishFetchingEvents which will set the events in the Redux state.

export function* fetchAllEventsEffect(action): * {
    try {
        const response = yield call(fetchEvents);
        yield put(finishFetchingEvents(response.data));
    } catch (e) {
        console.error("Could not load events", e);
        yield put(setError(e));
    }
}

export default function* fetchAllEvents(): * {
    yield takeLatest(FETCH_EVENTS, fetchAllEventsEffect);
}

export function fetchEvents() {
    return axios.get('http://localhost:8080/allevents');
}

Context

The state is maintained in the Context and being handed over to the component using a Provider. When the getEvents method is called, the state on the Context will be updated with the response from the server.

State

const EventContext = createContext();

const EventState = props => {
    const initialState = {
        events: [],
        loading: false
    };

    const [state, dispatch] = useReducer(EventReducer, initialState);

    const getEvents = async (name) => {
        setLoading();
        const result = await axios.get('http://localhost:8080/allevents');
        const events = result.data;
        dispatch({
            type: GET_EVENTS,
            payload: name ? events.filter(item => (item.name.toLowerCase()).includes(name)) : events
        })
    };

    const setLoading = () => dispatch({type: SET_LOADING});

    return (
        <EventContext.Provider
            value={{
                events: state.events,
                loading: state.loading,
                getEvents,
            }}>
            {props.children}
        </EventContext.Provider>
    )
};

Reducer

export const GET_EVENTS = 'GET_EVENTS';
export const SET_LOADING = 'SET_LOADING';

export default (state, action) => {
    switch (action.type) {
        case GET_EVENTS:
            return {
                ...state,
                events: action.payload,
                loading: false
            };
        case SET_LOADING:
            return {
                ...state,
                loading: true
            };
        default:
            return state
    }
}

Getting the state from Redux using containers

export const mapStateToProps = (state) => ({
    events: state.eventDetails.events,
});

Getting the state from Context using hooks

  const {events, loading, getEvents} = useContext(EventContext);
  useEffect(() => { getEvents();}, []);

My opinion is that for new projects where you need to maintain the state, Context API is a better choice as it makes things a bit simpler than Redux. However, in projects where you are already using Redux, a migration to Context might not really be needed, as in the end both do implementation do the same thing, maintaining state.

You can clone the sample project from here.
In order to start the project you can follow the steps in the Readme. For the calls to server we will be using the mockserver.

Let me know in the comments section below if you already use the Context API and if you find it better than Redux and why.

💖 💪 🙅 🚩
kittyradulescu
Cristina Radulescu

Posted on April 24, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related