Skip to main content

Getting started with react-redux

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.
  • 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
These are the main players we need to know, we need to understand. Next, we will use the react-redux library to accomplish this.

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
Ok, that seems like a lot but it is not. The code is less, but yes we need to make sure to follow these steps. That is why it looks complex.
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)
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-reference
https://redux.js.org/api/createstore

Comments