DEV Community

Priya Kothalkar
Priya Kothalkar

Posted on

I Replaced 10 useStates with One useReducer (ft. HackerRank Challenge)

The Challenge

Today, I came across a challenge on hackerRank, focused on State management and it turned out to be interesting + to test my Basics!
Link to the HackerRank Challenge

Here are the details:
We have 5 different feedback cards labelled as:

  1. Readability
  2. Performance
  3. Security
  4. Documentation
  5. Testing

Each card has 2 buttons as Upvote and Downvote.

The Goal:

All cards should render together, but each card must manage its upvote/downvote count separately, without affecting the others. Along with it, they should pass the tests on HackerRank.

Use Case:

  1. Clicking Upvote on a card should increase that card’s upvote count by 1
  2. Clicking Downvote should increas that card’s downvote count by 1

Each card is independent, and the count should reflect it.

Let's explore the Solution:

Solution 1: Use 10 individual useState Hooks.
Here, I’d maintain a separate state for:

  • Each card’s upvotes (5 total)
  • Each card’s downvotes (5 total)
const [readabilityUp, setReadabilityUp] = useState(0);
const [readabilityDown, setReadabilityDown] = useState(0);
// ...and so on for the other 4 cards
Enter fullscreen mode Exit fullscreen mode

Pros:

  • Simple to implement
  • Easy to follow for beginners

Cons:

  • Not scalable — 10 states for just 5 cards!
  • Becomes messy with more feedback types
  • Logic is duplicated across cards

Solution 2: Using useReducer for centralised state
Instead of managing 10 different states, I centralized state handling using a reducer.

  1. Initial State:
const initialState = {
  Readability: { upvote: 0, downvote: 0 },
  Performance: { upvote: 0, downvote: 0 },
  Security: { upvote: 0, downvote: 0 },
  Documentation: { upvote: 0, downvote: 0 },
  Testing: { upvote: 0, downvote: 0 },
}
Enter fullscreen mode Exit fullscreen mode
  1. Reducer Logic:
function reducer(state, action) {
  const { card, votetype } = action.payload;
  switch (votetype) {
    case "upvote":
      return {
        ...state,
        [card]: {
          ...state[card],
          [votetype]: state[card][votetype] + 1
        }
      };
    case "downvote":
      return {
        ...state,
        [card]: {
          ...state[card],
          [votetype]: state[card][votetype] + 1
        }
      };
    default:
      return state;
  }
}
Enter fullscreen mode Exit fullscreen mode

Pros:

  • Scalable and clean
  • Centralised state handling
  • DRY (Don't Repeat Yourself) principle applied
  • Easier to debug and maintain

Cons:

  • Slightly more advanced if you're new to React
  • Requires understanding of reducers and dispatching actions

My Learning & Takeaways
I started with useState, but then realised it isn't maintainable and Scalable. Once I refactored using useReducer, the entire structure became cleaner, modular, scalable and better!

What I learned:
Think beyond “just getting it to work”, consider scalability and maintainability.
useReducer is a great fit when managing multiple related states. Even simple UI components can challenge your architecture choices

Final Thoughts:

This small challenge helped me rethink how I approach state in React.
It was more than just fixing a UI; it became an exercise in writing cleaner, scalable code.

Would love to hear how you would have solved this! Let’s discuss 💬

Top comments (1)

Collapse
 
trojanmocx profile image
ALI

Ah yes, the eternal React journey: start with useState because it feels friendly, then suddenly you’re buried under 10 states like an overworked intern juggling sticky notes. Switch to useReducer, and boom—you’ve basically written a mini Redux but without the corporate trauma. Next step: accidentally reinventing React Query just to track upvotes