So as my first post, so hoping for a lot and lot of positive criticism. I will write about why I like zustand over redux. Both of them are in a way, exactly same, both are unidirectional state managers, but why is zustand better (more like why I like zustand more), keep reading to know.. haha, big suspense movies' fan here.
So we will start with an example of how to make a simple counter in redux:
Let's first of all create a react app.
npx create-react-app redux-tut
and now installing the dependencies needed
npm install @redux/toolkit react-redux
We have to install these 2 dependencies, to be able to use redux toolkit, which is supposed to be the latest and with least boilerplate.
Now in redux toolkit, one store has many slices, and every one of those slices, has their reducers and their actions and everything.
Now we will create a new slice for our counter.
Lets create a store folder in our src folder and a counterSlice.js folder in our store folder.
and paste this in your counterSlice.js
import { createSlice } from "@reduxjs/toolkit";
export const slice = createSlice({
name: "counter",
initialState: {
value: 0
},
reducers: {
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
incrementByAmount: (state, action) => {
state.value += action.payload;
}
}
});
export const { increment, decrement, incrementByAmount } = slice.actions;
export const selectCount = (state) => state.counter.value;
export default slice.reducer;
Now in this counterSlice, we have 3 actions, increment and decrement by 1 and increment by value, and we can set the value.
and lets create a store and import this slice into that store.
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "./counterSlice";
export default configureStore({
reducer: {
counter: counterReducer
}
});
and now creating the Counter.js
import React, { useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import {
decrement,
increment,
incrementByAmount,
selectCount,
} from "../store/counterSlice";
export function Counter() {
const count = useSelector(selectCount);
const dispatch = useDispatch();
const [incrementAmount, setIncrementAmount] = useState("2");
return (
<div>
<div>
<button
aria-label="Increment value"
onClick={() => dispatch(increment())}
>
+
</button>
<span>{count}</span>
<button
aria-label="Decrement value"
onClick={() => dispatch(decrement())}
>
-
</button>
</div>
<div className>
<input
aria-label="Set increment amount"
value={incrementAmount}
onChange={(e) => setIncrementAmount(e.target.value)}
/>
<button
onClick={() =>
dispatch(incrementByAmount(Number(incrementAmount) || 0))
}
>
Add Amount
</button>
</div>
</div>
);
}
and last but not the least, we have to import this store in the in the provider that we have to keep at the top most level, so as a parent of App.
so go to the index.js for your app and.
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import store from "./store/store";
import { Provider } from "react-redux";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
add this provider.
You can check this non-css redux-tut here.
Now seems easy af anyway, but lets have a look at the competitor, zustand, which has typescript support by the way.
Will start from the basics, lets create
npx create-react-app zustand-tut
and the dependicies
npm install zustand
yes thats right, thats all the dependicies we need.
now lets create a store. This time the folder structure is.
and now paste this in store/counterStore.js:
import create from "zustand";
export const useCounterStore = create((set) => ({
count: 0,
incrementBy1: () => set((state) => ({ count: state.count + 1 })),
decrementBy1: () => set((state) => ({ count: state.count - 1 })),
incrementByVal: (val) => set((state) => ({ count: state.count + val }))
}));
and in counter.js, this:
import React, { useState } from "react";
import { useCounterStore } from "../store/counterStore";
export function Counter() {
const { count, incrementBy1, decrementBy1, incrementByVal } = useCounterStore(
(state) => state
);
const [incrementVal, setIncrementVal] = useState(5);
const incrementByValHandler = (e) => {
incrementByVal(Number(incrementVal));
};
return (
<div>
<div>
<button aria-label="Increment value" onClick={incrementBy1}>
+
</button>
<span>{count}</span>
<button aria-label="Decrement value" onClick={decrementBy1}>
-
</button>
</div>
<div className>
<input
aria-label="Set increment amount"
value={incrementVal}
onChange={(e) => setIncrementVal(e.target.value)}
/>
<button onClick={incrementByValHandler}>Add Amount</button>
</div>
</div>
);
}
And here we dont have to wrap it in provider as well.
and thats it. Lets try this on here.
Now as we can see, much less boilerplate, no dispatches, no providers, much more readable, and last but not least, much less code. Now 1 thing that is left is performance, for that I will write another article, in the mean time, if you wanna just trust my word, I like zustand more..
Thank you everyone.
Different type of state managers' articles coming soon.
Top comments (0)