This content originally appeared on DEV Community and was authored by Borislav Hadzhiev
Things to look at when typing reducers
There are a couple of things that make typing reducer functions in react a little less error prone and give you some nice type checking and auto complete capabilities.
Among them are:
- Using enums for action types
- Using unions for actions
- Being explicit abut the reducer's return value
- Throw an error for the default case - if you didn't handle it, most likely it wasn't intended (even though we are using typescript and we should never mistype - the default case is almost never what you want)
Example
Let's look at a simple copy-paste style example in a single file called App.tsx
and go over some of the key points:
import {useEffect, useReducer} from 'react';
type UserAttrs = {
gender?: string;
name?: string;
country?: string;
email?: string;
};
type UserState = {
loading: boolean;
error: string | null;
data: UserAttrs;
};
enum ActionType {
FETCH_USER = 'fetch_user',
FETCH_USER_SUCCESS = 'fetch_user_success',
FETCH_USER_ERROR = 'fetch_user_error',
}
type Action =
| {type: ActionType.FETCH_USER}
| {
type: ActionType.FETCH_USER_SUCCESS;
payload: Required<UserAttrs>;
}
| {type: ActionType.FETCH_USER_ERROR; payload: string};
function reducer(_state: UserState, action: Action): UserState {
switch (action.type) {
case ActionType.FETCH_USER: {
return {loading: true, error: null, data: {}};
}
case ActionType.FETCH_USER_SUCCESS: {
return {loading: false, error: null, data: action.payload};
}
case ActionType.FETCH_USER_ERROR: {
return {loading: false, error: action.payload, data: {}};
}
default: {
throw new Error(`Unhandled action type - ${JSON.stringify(action)}`);
}
}
}
function App() {
const [{loading, error, data}, dispatch] = useReducer(reducer, {
loading: false,
error: null,
data: {},
});
useEffect(() => {
const fetchUser = async () => {
dispatch({type: ActionType.FETCH_USER});
try {
const res = await fetch('https://randomuser.me/api/');
const parsed = (await res.json()) as {
results: [
{
gender: string;
name: {first: string};
location: {country: string};
email: string;
},
];
};
const user = parsed.results[0];
dispatch({
type: ActionType.FETCH_USER_SUCCESS,
payload: {
gender: user.gender,
name: user.name.first,
country: user.location.country,
email: user.email,
},
});
} catch (error) {
if (error instanceof Error) {
dispatch({type: ActionType.FETCH_USER_ERROR, payload: error.message});
} else {
dispatch({
type: ActionType.FETCH_USER_ERROR,
payload: 'Something went wrong',
});
}
}
};
fetchUser();
}, []);
return (
<div>
{loading && <h3>Loading...</h3>}
{error && <h1 style={{color: 'red'}}>{error}</h1>}
<h1>
<pre>{JSON.stringify(data, null, 2)}</pre>
</h1>
</div>
);
}
export default App;
The first thing to look at is using an enum
for the ActionType
variables. You don't want to be duplicating action type strings at the case
statements, nor in the dispatch
calls, you could easily mistype an action and you don't get the nice autocomplete from typescript.
For our Action
type we use a union
. Typing the action
as a union allows us to use the case
statement like a type guard
. In other words - if the action type is equal to ActionType.FETCH_USER_ERROR
- you must provide a payload parameter of type string
, otherwise typescript will error out.
In the default
case of our reducer we simply throw an error, the default
case is almost never what you want - it better to be explicit - if we dispatched an action which wasn't handled we should know about it - it's
strange an not intuitive.
When dispatching
actions always use the ActionType
enum for auto completion and to avoid typos.
This post was originally published at bobbyhadz.com on February 26, 2021.
This content originally appeared on DEV Community and was authored by Borislav Hadzhiev

Borislav Hadzhiev | Sciencx (2021-02-26T18:27:24+00:00) Adding types to reducer functions. Retrieved from https://www.scien.cx/2021/02/26/adding-types-to-reducer-functions/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.