DEV Community

Hồng Phát
Hồng Phát

Posted on

Redux developers, please stop doing this!

I worked on a React project in 2019, I believe it was built on top of the react-boilerplate template, and the developer experience with Redux was so bad that I became a Vue developer.

After a few years working with Vuex, Pinia, Recoil (yes, I came back), and recently Jotai (which is my go-to state management library at the moment), a friend randomly asked for my help in debugging the flow of his app, and it amazed me how Redux developers still write that boilerplate code:

const cartSlice = createSlice({
  name: "cart",
  initialState: {
    items: [],
    total: 0,
    // x, y, z,…
  },
  reducers: {
    setTotal(state, action) {
      const { total } = action.payload;
      state.total = total;
    },
    pushItem(state, action) {
      const { item } = action.payload;
      state.items.push(item);
    },
    clearItems(state) {
      state.items.length = 0;
    },
    // setX, setY, setZ…
  },
});
Enter fullscreen mode Exit fullscreen mode

This was exactly the reason why I had such a bad time maintaining that Redux project. And it was even worse without Redux Toolkit (which they only introduced recently):

export const SET_TOTAL = "SET_TOTAL";

export const setTotal = (total) => ({
  type: SET_TOTAL,
  payload: total,
});

switch (action.type) {
  case SET_TOTAL: {
    return {
      ...state,
      total: action.payload,
    };
  }
  default:
    return state;
}

export const getTotal = createSelector(
  (state) => state.total,
  (total) => total
);

const mapStateToProps = (state) => ({
  total: getTotal(state),
});

const mapDispatchToProps = (dispatch) => ({
  setTotal: (total) => dispatch(setTotal(total)),
});

export default connect(mapStateToProps, mapDispatchToProps)(MyComponent);

class MyComponent extends Component {
  render() {
    const { total } = this.props;
    return <b>You must pay: {total}</b>;
  }
}
Enter fullscreen mode Exit fullscreen mode

This was not a joke! In fact, cumbersome, excessive boilerplate code like the example above was often considered "best practices" and "industry standards" in my observation. It was almost as if we were writing code like this:

function isDifferent(x, y) {
  return x != y;
}

function isGreater(x, y) {
  return x > y;
}

function subtract(x, y) {
  return x - y;
}

function greatestCommonDivisor(x, y) {
  while (isDifferent(y, 0)) {
    if (isGreater(x, y)) {
      x = subtract(x, y);
    } else {
      y = subtract(y, x);
    }
  }
  return x;
}
Enter fullscreen mode Exit fullscreen mode

The above code is far from being "highly scalable", providing "the best developer experience", or focusing on "performance and best practices".

Don’t get me wrong! Redux’s pattern itself is not bad; in fact, it’s so good that React introduced the useReducer hook to mimic it. The issue is that a marginal number of React developers are doing it wrong. Here’s how to do it right:

  • Batch relevant updates: Instead of creating data-oriented actions like PUSH_ITEM and SET_TOTAL, focus on behavior-oriented actions like ADD_TO_CART, which handle multiple updates (adding an item and updating the total) in one go.
  • Keep the state simple and move complex logic to selectors: For example, you can remove computable fields like total from your state entirely, then calculate them automatically from cart items using reselect. This approach not only ensures data integrity but also makes the code easier to maintain.

Please share this post with anyone you know who writes boilerplate-heavy Redux code, so we can all make state management in single-page applications less of a pain!

Top comments (0)