Okay, now that we’re Immer experts, let’s look at the two most important exports of the Toolkit: createSlice and configureStore.
createSlice: each top level object of your state is typically a slice
configureStore: abstracts away the pain that was createStore
Part 3: createSlice and configureStore
createAsyncThunk
On top of the included redux-thunk package, createAsyncThunk is the helper function for all your API call needs.
Coming from “legacy Redux”, this is the last basic piece of ReduxJS/Toolkit!
Part 4: createAsyncThunk
TypeScript
Keeping everything entirely type-safe.
Part 5: TypeScript
Reselect
More batteries included. You basically only need this when running into performance issues which is like, almost never (if you thought things through). Because JavaScript is just so damn fast these days.
Still, it’s a good idea to just use selectors from the get-go, that way, you will still not be running into re-rendering performance issues after the addition of 6 years of production data.
Part 6: createSelector
RTK Query
A much higher level of abstraction.
Okay, we’re getting to the meat of ReduxJS/Toolkit now. Yes, only now.
When migrating from classes, plain JavaScript, and all that other stuff that was hot back in the medieval ages, you may want to do the whole Immer, slices and asyncThunks stuff; but RTK Query might just be a tad too much in what probably already is a whole re-write.
There is pretty much an easy 1 to 1 conversion path there. When you want to go to all the way and also adopt RTK Query, you’ll have to change (a bit) more.
Will it be worth it in the end? Yes. Will it hurt? Also yes.
So maybe something you want to use for your next NEW React app 😉
Part 7: createApi (under construction!)
Not Covered
Parts of this series that will probably not be written.
Other Features
Since I already spent way too much time on this 😀
Listener Middleware:
Listener middleware
EntityAdapters:
The EntityAdapters are in fact quite handy. There is an example in moreSlice in the accompanying code. If you are doing basic CRUD, this can significantly reduce the boilerplate EVEN MORE.
import { createEntityAdapter, createSlice } from '@reduxjs/toolkit';
const usersAdapter = createEntityAdapter({
// In case "id" is not the name of the id property
selectId: (user: User) => user.name,
// Sorter for the "All IDs" array
sortComparer: (a, b) => a.name.localeCompare(b.name),
});
export const usersSlice = createSlice({
initialState: usersAdapter.getInitialState(),
// initialState: {
// ids: Id[],
// entities: Record<Id, T>,
// }
reducers: {
addUser: usersAdapter.addOne,
addUsers: usersAdapter.addMany,
updateUser: usersAdapter.updateOne,
removeUser: usersAdapter.removeOne,
// ... and many more fns ready for use
}
});
autoBatchEnhancer:
autoBatchEnhancer is the built-in (and automatically registered) mechanic to delay low-priority actions.
Best Practices
A blog post that has been sitting on my todo list for the longest time is “Redux: The Good, The Bad and The Boilerplate”.
ReduxJS/Toolkit proves that you don’t need the horror that is your typical NGRX implementation. Your average enterprise backend developer has to do something in the frontend and after the initial pants pissing you end up with a custom selector for EVERYTHING topped with an Effect where the failure action doesn’t do anything (or could be replaced with a single global interceptor) sprinkled with a few Redux no-nos on top.
Yes, I’m quite frustrated by this boilerplate²².
Aside from the rant, that blog post would be something that explains basic Redux followed by what is basically the Redux Style Guide.