Redux helps us centralize and manage our app data globally without having to lift state to parent components and eliminates the problems of props drilling. Not every app needs to use redux, but as your app grows using redux is inevitable.
We now have redux-toolkit which has become the official recommendation for writing redux logic. To know the pros of using redux-toolkit, we should know the cons of using the traditional Redux code. Since redux-toolkit is just a layer of abstraction above the traditional code, we should know the underlying architecture of Redux, so that we can easily gauge the problems it solves.
Before using Redux Toolkit, let us understand how to set up/use redux using in a react app, and then in the upcoming blog, we will discuss how Redux toolkit reduces the boilerplate code and acts as an abstraction.
1. Initialize your react app:
Open a terminal and enter the following cmd to initialize your project with a predefined template
$ npx create-react-app react-redux-demo
and then cd to your project directory and run
npm start
2. Install redux and react-redux:
React Redux lets your React components talk to Redux store, and send data(fancy word - dispatch actions) to the store so that it can be updated as per the action performed. Enter the following command to do so:
npm install redux react-redux
3. Creating a redux store:
We will create two folders:
redux (files related to redux configuration)
store (files related to redux store)
Inside the redux folder, we will create a file createStore.ts
.
Bare minimum createStore.ts file:
import rootReducer from "./reducers";
import { createStore } from "redux";
const store = createStore(rootReducer);
export { store };
We use createStore from redux to create a store for our app and export the store from thsi file.
The rootReducer used in this file will be discussed later.
4. Providing this store to our React app:
Now since we have the store ready, we need a way for our components to access it. We will use Provider from react-redux to do so.
At the root level of our app, we will create a file called AllProvider.tsx (this file acts as the wrapper for our react components)
import GlobalStyle from "styles";
import App from "./App";
import { Provider } from "react-redux";
import { store } from "redux/createStore";
const AllProvider = () => {
return (
<Provider store={store}>
<GlobalStyle />
<App />
</Provider>
);
};
export default AllProvider;
Now since we have wrapped our App with the Provider, our app can communicate/access data stored in the redux store. Now let's try to figure out a way to put some data inside the store.
5. Putting data inside our store:
To create/update/delete data inside the store, we need to dispatch actions from React components and then based on the dispatched action we will perform some operations.
Dispatching actions
Let us consider an app for adding posts(not a very useful app though but does the work here).
We will create three folders inside our store folder:
types folder
In this folder, we will create a file called postTypes.ts
const ADD_POST = "ADD_POST";
export const PostTypes = {
ADD_POST,
};
Here we define the types/use-cases which can be performed. We have created a type ADD_POST here so that this type can be used by the action creator when dispatching an action and this type can be used by the reducer to accept this as a case. We will use the same type constant in the reducers and actions file to avoid any typos.
actions folder
We will create a file called postActions.ts:
import { PostTypes } from "store/types/postTypes";
export const addPost = (post: any) => ({
type: PostTypes.ADD_POST,
post,
});
Here we create an action creator addPost which returns the action. (An action is simply an object that has two things: a type, and a payload. An action creator is simply a function, that just returns an action.) This is the function that will send data to our store.
reducers folder
This folder holds the traditional switch case statements to help us identify how the state will be updated based on the types:
import { PostTypes } from "store/types/postTypes";
const INITIAL_STATE: any[] = [];
const postReducer = (state = INITIAL_STATE, action: actionMapType) => {
switch (action.type) {
case PostTypes.ADD_POST: {
const updatedState = [...state];
updatedState.push(action.post);
return updatedState;
}
default:
return state;
}
};
export default postReducer;
Now that we have the reducer ready, let's pass this reducer to our store. Remember we had used rootReducer while creating our store, now you know where that rootReducer came from, but still, there's a catch. In a real-world application, we never are going to have a single reducer(if this is the case please use component state instead). So for an app that has multiple reducers we will combine the reducers(yes literally combineReducers) as one and pass it to our store, like so:
Inside our redux folder, we will create a file called reducer.ts
import { combineReducers } from "redux";
import postReducer from "store/reducers/postReducer";
const rootReducer = combineReducers({
posts: postReducer,
});
export default rootReducer;
export type RootState = ReturnType<typeof rootReducer>;
Voila, we have our redux store ready to give and receive data, using useSelector() (similar to mapStateToProps in class components) and useDispatch() (similar to mapDispatchToProps in class components) respectively. Let's have a look at how to do that and we will be good:
import { useDispatch } from "react-redux";
import { CanvasWrapper } from "./styles";
import { addPost } from "store/actions/postActions";
import { RootState } from "redux/reducers";
const Canvas = () => {
const dispatch = useDispatch();
const posts = useSelector((state: RootState) => state.posts);
const addPostHandler = () => {
const newPost = {
title: "Hello this is a post",
};
dispatch(addPost(newPost));
};
return (
<CanvasWrapper>
<button onClick={addPostHandler}>Add post</button>
</CanvasWrapper>
);
};
export default Canvas;
We dispatch addPost() to send a new post to the store and then the action goes to the reducer and the case which matches the type of the action is executed. We access the posts inside the store using useSelector(). The key name would be the same as we define in the combineReducers().
So this is how we connect redux with our react components using react-redux. The above code could be too much for newbies and might feel overwhelming, so we would in the next blog how redux-toolkit solves so many things for much and makes it easier to use redux.
Thanks and Happy Coding...
Top comments (1)
yes, you can definitely do that