DEV Community

Rahul Sharma
Rahul Sharma

Posted on • Updated on

React redux best practice to reduce code

What is redux?

Redux is a JavaScript library for managing the application state. It is a predictable state container that allows you to write applications that behave consistently no matter what changes the state.

Most of us are using redux in multiple projects, I've also used it in multiple projects. It's easy to use, and it's easy to understand, One thing I don't like is a lot of boilerplate code. Let's try to understand with a simple example.

I'm not going to cover redux setup details here

Step:
Install react-redux and redux using npm

// Store.ts
import { combineReducers, createStore } from "redux";

// Reducer file
const counterReducer = (state = 0, { type, payload }) => {
  switch (type) {
    case "INCREMENT":
      return state + payload;
    case "DECREMENT":
      return state + payload;
    default:
      return state;
  }
};

// Action creator file
export const increment = (payload) => ({ type: "INCREMENT", payload });
export const decrement = (payload) => ({ type: "DECREMENT", payload });

// Store entrypoint file
const reducers = () =>
  combineReducers({
    counter: counterReducer,
  });

const store = createStore(reducers());

export default store;
Enter fullscreen mode Exit fullscreen mode
NOTE: every section can be moved to a different file. I'm trying to keep it simple.

App.ts

export default function App() {
  const state = useSelector((state: any) => state);
  const dispatch = useDispatch();
  return (
    <div>
      <h1>Count: {state.counter}</h1>
      <button onClick={() => dispatch(increment(1))}>Increment</button>
      <button onClick={() => dispatch(decrement(-1))}>Decrement</button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

index.ts

<Provider store={store}>
  <App />
</Provider>
Enter fullscreen mode Exit fullscreen mode

You might be wondering what is wrong with this approach. There is no problem with this approach In this example, We have only 2 actions it looks simple but in the real world, we have a lot of actions. You have to create a separate action creator function for all. I don't like it because all the function has almost the same code except the action name. It would be great if all the action creators can be generated automatically.


How can we generate action creators automatically?

The first thing you have to do is update the structure of your reducer, Instead of using a switch case, you should use an object.

An object is a better way to manage your reducer, It's faster and easier to read. I don't like switch cases, I prefer objects.

Switch can have a time complexity of O(n) where n is the number of cases. An object has a time complexity of O(1). check here some other best practices

Reducer file

const counterReducerMap = {
  increment: (state, { payload }) => state + payload,
  decrement: (state, { payload }) => state + payload,
};

const counterReducer = (state = 0, action) => {
  const handler = counterReducerMap[action.type];
  return handler ? handler(state, action) : state;
};
Enter fullscreen mode Exit fullscreen mode

Let's create generic action creator function

const createAction = <T>(reducers): T => {
  return Object.keys(reducers).reduce((acc, type) => {
    acc[type] = (payload) => ({
      type,
      payload,
    });
    return acc;
  }, {} as T);
};

export const { increment, decrement } = createAction(counterReducerMap);
Enter fullscreen mode Exit fullscreen mode

You can remove generic type if you are using javascript.

NOTE: Import thing to notice here is reducer map keys are your action creator functions. It's not a reducer function.

If you follow this approach, you can reduce a lot of boilerplate code. This approach will also reduce production bundle size.

Bonus for typescript developers
type ActionCreator<A> = {
  [key in keyof A]: <T>(payload: T) => {
    type: key;
    payload: T;
  };
};

type Action = ActionCreator<typeof counterReducerMap>;
export const { increment, decrement } = createAction<Action>(counterReducerMap);
Enter fullscreen mode Exit fullscreen mode

Live Example: here


Thank you for reading 😊

Got any questions or additional? please leave a comment.


Must Read If you haven't
React best practices and patterns to reduce code - Part 1
3 steps to create custom state management library with React and Context API
How to cancel Javascript API request with AbortController
13 Typescript Utility: A Cheat Sheet for Developer

More content at Dev.to.
Catch me on YouTube, Github, Twitter, LinkedIn, Medium, Stackblitz, Hashnode, HackerNoon, and Blogspot.

Top comments (2)

Collapse
 
1antares1 profile image
1antares1

Great article!

Collapse
 
sahil_verma_77835e05a3c5b profile image
Sahil Verma

One doubt here:

  • Will this work for all edge cases like in some where we need to pass one more param like meta to our action?