I don't like Redux because of its complexity and dozens of lines of boilerplate code. So, I've never used it in production. When I worked on existing Redux-projects that someone left to me (you know, these aren't perfect projects ðĪŠ), I always replaced Redux with MobX, and the code became clearer at times.
But today I tried Redux Toolkit, an official toolset for efficient Redux development, and now I want to try it in production. In this post, I'm going to compare MobX and Redux Toolkit in terms of their simplicity. I'm not a Redux-guy, so feel free to point out my mistakes, if any.
Ok, here we go!
I won't show you a gradual transition from "Vanilla Redux" to Redux Toolkit, because you can find it in the docs. I just show you a simple "slice" (the equivalent of MobX store) with a counter.
export const counterSilce = createSlice({
name: 'counter',
initialState: 0,
reducers: {
increment: (state) => state + 1,
decrement: (state) => state - 1,
},
})
export const store = configureStore({
reducer: counterSilce.reducer,
})
You may ask, "What? Where is my ugly favorite action creators? Is it Redux?" At least I had just such questions a couple of hours ago.
But how can you use it?
It's simple, just like a regular store.
const Component = () => {
const dispatch = useDispatch()
const counter = useSelector((counter) => counter)
return (
<section>
<button onClick={() => dispatch(counterSilce.actions.decrement())}>
-
</button>
<span>{counter}</span>
<button onClick={() => dispatch(counterSilce.actions.increment())}>
+
</button>
</section>
)
}
And now look at the MobX store. Very similar, isn't it?
// MobX store
export const counterStore = () => ({
counter: 0,
increment() {
this.counter += 1
},
decrement() {
this.counter -= 1
},
})
Fetching data
Now I'll show you a little more complex example: fetching data. I'll use JSONPlaceholder API to fetch a list of users with Redux Toolkit.
Firstly, let me configure the slice. I'll store the loading
flag and array of users
in the state (error handling omitted for simplicity).
export const usersSlice = createSlice({
name: 'users',
initialState: {
loading: false,
users: [],
},
reducers: {},
})
Here will be two reducers: one for setting loading
to true (before fetching) and second for updating users
(after fetching).
export const usersSlice = createSlice({
// ...
reducers: {
getUsersStart: (state) => ({ ...state, loading: true }),
getUsersSuccess: (state, action) => ({
...state,
loading: false,
users: action.payload.users.map((u) => ({
id: u.id,
name: u.name,
username: u.username,
})),
}),
},
})
And I'll use an action creator which returns a function with dispatch
for the request itself.
const fetchUsers = () => (dispatch) => {/* ... */}
But wait, we need a thunk
middleware for this, right? Indeed. But Redux Toolkit took care of that by including some middleware by default, so we don't need to install it manually.
The logic of the fetching function is simple: enable loading
, make HTTP request, disable loading
, update users
array.
export const fetchUsers = () => (dispatch) => {
try {
// enable 'loading'
dispatch(usersSlice.actions.getUsersStart())
// make HTTP request
fetch('https://jsonplaceholder.typicode.com/users')
.then((r) => r.json())
.then((users) => {
// disable 'loading', update 'users' array
dispatch(usersSlice.actions.getUsersSuccess({ users }))
})
} catch (error) {
console.error(error)
}
}
And finally, we need to connect all this logic to React.
const Component = () => {
const dispatch = useDispatch()
const usersStore = useSelector((store: Store) => store)
useEffect(() => {
dispatch(fetchUsers())
}, [])
return (
<main>
{usersStore.loading ? (
<span>Loading...</span>
) : (
usersStore.users.map((u) => (
<div key={u.id}>
<span>{u.name}</span>
<span>{u.username}</span>
</div>
))
)}
</main>
)
}
You can find all these examples in my GitHub repo.
Yeah, Redux Toolkit is an amazing tool for Redux. Just try it and share your impressions.
Top comments (1)
Hey Viacheslav, great article.
I've been trying to decide between MobX vs Redux Toolkit for my upcoming project. I'd say I'm comfortable with both and they end up being kind of similar. Based on your experience with both, which would you prefer these days?