Redux without boilerplate
↳ React
04 Mar 2020 | 6 minute read
Redux is a wonderful library for managing state. It's fast, deterministic and requires little configuration to get started with. But as your site grows larger and larger, chances are that your Redux reducers
, actions
and initial states
makes your project bloated.
If you're using Typescript, this is apparent early, as you're most probably also declaring types for your reducers
, actions
and states
as well. It the end, you might have to update about 6 files to implement a new feature.
This becomes cumbersome after a while and could be a potential source of frustration and unease, as it becomes harder to keep track of required changes when you're implementing features.
To tackle this issue, and to make development in Redux more efficient, there is a neat (official) package called Redux Toolkit.
Introduction to Redux Toolkit
To explore Redux Toolkit, let's build a basic reducer
for our dummy e-commerce. To display the products for our customers, we need a state that holds all of our products, and we need to be able to update how many products we have in stock (onHand
) for each product.
First of all, let's declare the types for our products
, state
and initialState
.
interface Product {
sku: string;
onHand: number;
}
export interface ProductsState {
products: Product[];
}
const initialState: ProductsState = {
products: [],
};
Now, let's implement our reducer
. To do so, we will utilise the createSlice from the Redux Toolkit library. We'll also implement a case reducer
function to update the onHand value for a specific product as well.
import { createSlice } from '@reduxjs/toolkit';
const productsSlice = createSlice({
name: 'products',
initialState: initialState,
reducers: {
updateOnHand(state, action): void {
const { sku, onHand } = action.payload;
const product = state.products.find(
(product): boolean => product.sku === sku,
);
if (product) {
product.onHand = onHand;
} else {
state.products.push({ sku, onHand });
}
},
},
});
Putting it all together, and exporting our actions
and reducers
, our final code looks like this:
import { createSlice } from '@reduxjs/toolkit';
interface Product {
sku: string;
onHand: number;
}
export interface ProductsState {
products: Product[];
}
const initialState: ProductsState = {
products: [],
};
const productsSlice = createSlice({
name: 'products',
initialState: initialState,
reducers: {
updateOnHand(state, action): void {
const { sku, onHand } = action.payload;
const product = state.products.find(
(product): boolean => product.sku === sku,
);
if (product) {
product.onHand = onHand;
} else {
state.products.push({ sku, onHand });
}
},
},
});
export const { updateOnHand } = productsSlice.actions;
export default productsSlice.reducer;
Using Redux Toolkit, all of our types
, actions
and reducers
are placed in one file. Neat!
If you're a Typescript developer with a keen eye, you might have noticed that our actions aren't typed in the example above. Redux Toolkit has great support for usage with Typescript, and I invite you to dive deeper into how they can be used together.