Greetings!
Redux is described as a predictable state container for JS Apps (in the docs) and a few more features. What we need to realize is that it gives a single source of truth for our JavaScript applications. Instead of saving our global states in multiple places, we manage them in a centralized location. That is it! Let's jump.
To make things more clear, I will create multiple files for actions, states, and reduces but instead one can follow the duck pattern to make everything in a single file.
Note that using the redux toolkit can simplify this further. However, I do not use it in this article because I feel that when starting this gives a clear picture.
Redux is described as a predictable state container for JS Apps (in the docs) and a few more features. What we need to realize is that it gives a single source of truth for our JavaScript applications. Instead of saving our global states in multiple places, we manage them in a centralized location. That is it! Let's jump.
- Single source of truth
- Share data across multiple places
The source code for this article can be found here.
The redux architecture
It is this redux architecture we need to understand. After that, it is how to accomplish it in reactjs way.- view - any part of the user interface
- action - what sort of thing the user did in the user interface?
- reducer - this is where the data manipulation happens. It captures the action and acts upon that, updates the store
- store - this is where the all data is saved just like a database
The steps
Instead of directly jumping into code, let's see what things we need to do.- Install redux, react-redux, and reselect libraries
- Wrap the App with the store provider
- Initialize the store
- Define actions
- Implement reducer
- Implement select
To make things more clear, I will create multiple files for actions, states, and reduces but instead one can follow the duck pattern to make everything in a single file.
Note that using the redux toolkit can simplify this further. However, I do not use it in this article because I feel that when starting this gives a clear picture.
What we build
It is the same counter application with increment and decrement buttons.Initialize the project
I use the create-react-app for this.create-react-app react-redux-example
cd react-redux-example
npm install redux react-redux reselect
Our
final file structure will look like this.The Provider
We need to wrap the App with Provider in index.js so that the redux store is
available in the whole application. (index.js)
Happy learning guys ☺
https://redux.js.org/api/createstore
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import { Provider } from "react-redux";
import store from "./store";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
Initial state
We are counting, hence we have stored counter property. (store/counter/initial-state.js)export const initialState = {
count: 0,
};
Actions
We define two actions to increment and decrement the counter. (store/counter/action.js)export const INCREMENT = "counter/increment";
export const DECREMENT = "counter/decrement";
export const increment = () => {
return { type: INCREMENT };
};
export const decrement = () => {
return { type: DECREMENT };
};
Reducer
It is reducer who changes the store by acting according to an action. (store/counter/reducer.js)import { DECREMENT, INCREMENT } from "./action";
import { initialState } from "./initial-state";
const counterReducer = (state = initialState, { type }) => {
switch (type) {
case INCREMENT: {
return {
...state,
count: state.count + 1,
};
}
case DECREMENT: {
return {
...state,
count: state.count - 1,
};
}
default:
return state;
}
};
export { counterReducer };
Select
We define a separate select as well. This will remove any duplicate codes in selections. (store/counter/selector.js)import { createSelector } from "reselect";
const selectCounter = (state) => state.counterReducer;
const selectCount = createSelector(selectCounter, (counter) => counter.count);
export { selectCount };
Root reducer
Instead of having reducers inside the store, it will be better to create a single reducer by combining all. (store/root-reducer.js)import { combineReducers } from "redux";
import { counterReducer } from "./counter/reducer";
export const rootReducer = combineReducers({
counterReducer,
});
Store
Finally, the store itself. (store/index.js)import { legacy_createStore as createStore } from "redux";
import { rootReducer } from "./root-reducer";
const store = createStore(rootReducer);
export default store;
Counter component
And next, our Counter component that uses the store. (counter/Counter.jsx)import { useDispatch, useSelector } from "react-redux";
import { decrement, increment } from "../store/counter/action";
import { selectCount } from "../store/counter/selector";
const Counter = () => {
const count = useSelector(selectCount);
const dispatch = useDispatch();
const handleIncrement = () => dispatch(increment());
const handleDecrement = () => dispatch(decrement());
return (
<>
<h3>Count: {count}</h3>
<button onClick={handleIncrement}>Increment</button>
<button onClick={handleDecrement}>Decrement</button>
</>
);
};
export { Counter };
That
is it! run and see how it works.Is that all?
Not really. It is somewhat misleading if we think this is the redux architecture as in real-world applications we need to access the backend. Front-end requests will go to the backend to save/fetch data and store it in the store. Let's do it in the next article.Happy learning guys ☺
References
https://redux.js.org/api/api-referencehttps://redux.js.org/api/createstore
Comments
Post a Comment