Context API vs. Redux in a React Application
Published: 2020-04-24 11:17:55

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##
```js
export const sagaMiddleware = createSagaMiddleware();
const store = createStore(eventReducer);
sagaMiddleware.run(rootSaga);

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

##Reducer##
```js
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.

```js
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. 

```js
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##

``` js
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##

```js
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#

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

#Getting the state from Context using hooks#

```js
  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](https://github.com/kittyradulescu/cristina.radulescu.utcn-gmail.com).
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.