Redux like State management without Redux

Yannick Wittwer
2 min readMay 11, 2021

In this post I would like to show you a way, to achieve Redux like State management with the Context API.

The example is based on a very simple calculator. To get started I would like to show you the Files used to define the Context.

/CalculatorContext
actionTypes.js
reducer.js
CalculatorProvider.jsx
useCalculator.js

actionTypes.js

The action types are just the constants to keep everything consistent. In our example those would be these two constants:

export const CALCULATOR_ADD = 'CALCULATOR_ADD';
export const CALCULATOR_SUBTRACT = 'CALCULATOR_SUBTRACT'

reducer.js

Just as you know it from redux you define the reducer as a switch with the action types you defined in the actionTypes.js

export const calculatorReducer = (state, action) => {
switch (action.type) {
case CALCULATOR_ADD:
return updateState(state, {
total: state.total + action.data,
history: [
...state.history,
state.total
]
});
case CALCULATOR_SUBTRACT:
return updateState(state, {
total: state.total - action.data,
history: [
...state.history,
state.total
]
});
default:
throw new Error(`Action type ${action.type} not
available in calculator reducer`);
}
}

CalculatorProvider.jsx

The Provider is basically the heart of this context. First things first we create the context and define an initial state.

Inside the component, we use the useReducer hook together with a predefined reducer to get the state and the dispatch function.

Once we have the dispatch function available we create the two functions add and subtract, which are basically our action creators.

In the end, we just wrap all the values we need to interact with the Context into an Object and then wrap the children with the Context.

const initialCalculatorContext = {
total: 0,
history: []
};

export const CalculatorContext = React.createContext();

const CalculatorProvider = ({ children }) => {
const [state, dispatch] = React.useReducer(
calculatorReducer,
initialCalculatorContext
);

const add = (amount) =>
dispatchAction(dispatch, CALCULATOR_ADD, amount);
const subtract = (amount) =>
dispatchAction(dispatch, CALCULATOR_SUBTRACT, amount);

const value = {
state,
add,
subtract
};

return (
<CalculatorContext.Provider value={value}>
{children}
</CalculatorContext.Provider>
);
};

export default CalculatorProvider;

useCalculator.js

This hook is basically just for convenience reasons, you could also use the context without it.

export const useCalculator = () => {
const context = React.useContext(CalculatorContext);

if (context === undefined) {
throw new Error(`useCalculator must be used within
a CalculatorProvider`)
}

return context;
}

Now let’s use it

To use the context you will have to wrap your component first.

<CalculatorProvider>
<App />
</CalculatorProvider>

Afterward, you can use it via the hook in the following way.

const App = () => {
const {
state, add, subtract
} = useCalculator();

return <>SEE THIS CODE IN THE GITHUB REPO</>;
}

export default App;

If you would like to check out this example feel free to have a look at my Github Repo

This post is inspired by the awesome Kent C. Dodds and his Course named Epic React. Check it out here: https://epicreact.dev

--

--