๐ง The Brain of the Team: Building Your Own useReducer
Ever felt like your component has too many "mood swings"? One useState for loading, one for error, one for data... itโs a mess! In Bachata, if you try to lead five different moves at once, youโre going to trip. You need one clear signal. That is useReducer.
โฝ The "Starting Lineup" (The Problem)
Look at this. This is a team with no captain:
// Too many players, no coordination!
const [score, setScore] = useState(0);
const [isGameOver, setIsGameOver] = useState(false);
const [yellowCards, setYellowCards] = useState(0);
If the game ends, you have to remember to call setIsGameOver(true) AND maybe reset the score. If you forget one, it's a Red Card for your app's logic.
๐ ๏ธ Building the "Coach" (The Polyfill)
We are going to build our own version. Weโll use useState as the "legs" and useCallback to make sure the coach doesn't keep changing his mind (Stable References).
import { useState, useCallback, useRef, useEffect } from 'react';
function useOmarReducer(reducer, initialState) {
// 1. The actual storage (The Pitch)
const [state, setState] = useState(initialState);
// 2. Keep the logic fresh but stable
const reducerRef = useRef(reducer);
useEffect(() => {
reducerRef.current = reducer;
}, [reducer]);
// 3. The Dispatch (The Whistle)
// We use useCallback so this function stays the sameโ"1, 2, 3, Tap!"
const dispatch = useCallback((action) => {
setState((prevState) => reducerRef.current(prevState, action));
}, []);
return [state, dispatch];
}
Why this works:
-
useRef: Itโs like a notebook. We keep the latest tactics (the reducer) there so we don't get "Stale Closures." -
useCallback: This is key! It ensures thedispatchfunction doesn't change every render. If the function changed every time, it would be like a coach changing the rules every 5 minutes. No one can play like that!
๐ Putting it on the Pitch: The Match Day
Here is how we use our custom "Coach" to manage a Football match:
const matchReducer = (state, action) => {
switch (action.type) {
case 'GOAL':
return { ...state, score: state.score + 1 };
case 'FOUL':
return { ...state, yellowCards: state.yellowCards + 1 };
case 'WHISTLE':
return { ...state, isGameOver: true };
default:
return state;
}
};
function FootballMatch() {
const [match, dispatch] = useOmarReducer(matchReducer, {
score: 0,
yellowCards: 0,
isGameOver: false
});
return (
<div>
<h1>Score: {match.score} โฝ</h1>
<button onClick={() => dispatch({ type: 'GOAL' })}>GOAAAAL!</button>
<button onClick={() => dispatch({ type: 'FOUL' })}>Yellow Card ๐จ</button>
</div>
);
}
๐ Summary of the Tactics
| Term | The Football/Dance Way | The Code Way |
|---|---|---|
| Reducer | The Playbook / Tactics | The function that decides new state |
| Action | The Signal (e.g., "Pass!", "Turn!") | An object like { type: 'GOAL' }
|
| Dispatch | Blowing the Whistle | The function that triggers the change |
| State | The Current Score | The data sitting in your component |
๐ก Pro Tip
Never mutate the state directly! In Bachata, if you're supposed to be in position A and you suddenly teleport to B without the proper steps, the dance is ruined. In your reducer, always return a brand new object using the spread operator (...state). If you just change a property, React might not realize the "score" changed, and your UI will be stuck in the first half!
Would you like me to show you how to handle Asynchronous actions (like fetching data from a scouting API) with this setup? It gets a bit more "pro," but I know you can handle it!
โจ Let's keep the conversation going!
If you found this interesting, I'd love for you to check out more of my work or just drop in to say hello.
โ๏ธ Read more on my blog: bishoy-bishai.github.io
โ Let's chat on LinkedIn: linkedin.com/in/bishoybishai
๐ Curious about AI?:
You can also check out my book: Surrounded by AI
Top comments (0)