HomeAbout Me

Advanced React Patterns: State Initializer

By Daniel Nguyen
Published in React JS
July 07, 2025
2 min read
Advanced React Patterns: State Initializer

🔁 The State Initializer Pattern in React: Resetting with Confidence

One-liner: The State Initializer pattern allows you to initialize—and reliably reset—component state to its original value without surprises.

One of the fundamental building blocks of React is useState(). But sometimes, initializing state is only half the story—you also want a way to reset it.

The problem? Without proper care, that reset may not behave how you think it should.

Let’s dig into the State Initializer pattern, how to implement it correctly, and where it can go wrong. 🧠


🏁 The Basic Hook

Let’s start simple with a useCounter hook:

function useCounter() {
const [count, setCount] = useState(0)
const increment = () => setCount(c => c + 1)
return { count, increment }
}

Nothing special. It just counts. But what if we want to let users set the starting value?


🎚️ Adding Initial State

We can allow for configuration like this:

function useCounter({ initialCount = 0 } = {}) {
const [count, setCount] = useState(initialCount)
const increment = () => setCount(c => c + 1)
return { count, increment }
}

Now you can start your counter at 10, 50, or any value:

const { count } = useCounter({ initialCount: 10 })

Great! But now comes the kicker…


🔄 What About Reset?

We want to let users reset the count back to that initial value. That means we need to keep track of the original value:

function useCounter({ initialCount = 0 } = {}) {
const [count, setCount] = useState(initialCount)
const increment = () => setCount(c => c + 1)
const reset = () => setCount(initialCount)
return { count, increment, reset }
}

So what’s wrong with that?

⚠️ The Gotcha

Let’s say a parent component re-renders and passes a new initialCount value. Your hook will use the new value for resets—not the original one. 😬

That’s not what most people expect. The term “reset” implies going back to the state at the time the component was initialized—not the current value of the prop.


🧷 Locking in Initial State with Refs

The solution? Use a ref to capture the initial value once—and never let it change:

function useCounter({ initialCount = 0 } = {}) {
const initialCountRef = useRef(initialCount)
const [count, setCount] = useState(initialCountRef.current)
const increment = () => setCount(c => c + 1)
const reset = () => setCount(initialCountRef.current)
return { count, increment, reset }
}

🧠 useRef stores a value that persists across renders without causing re-renders. It’s perfect for capturing “once-on-mount” values like this.


🧪 Real Example: useToggle with Reducer

This pattern gets even more important when you manage state with a reducer. Here’s a simplified useToggle hook:

function toggleReducer(state, action) {
switch (action.type) {
case 'toggle': return { ...state, on: !state.on }
case 'reset': return { ...state, on: action.initialOn }
default: throw new Error('Unhandled action type')
}
}

And here’s the hook implementation:

function useToggle({ initialOn = false } = {}) {
const initialOnRef = useRef(initialOn)
const [state, dispatch] = useReducer(toggleReducer, {
on: initialOnRef.current,
})
const toggle = () => dispatch({ type: 'toggle' })
const reset = () => dispatch({ type: 'reset', initialOn: initialOnRef.current })
return { on: state.on, toggle, reset }
}

Now no matter how initialOn changes later from a parent component, your reset behavior is locked in to the original value passed.


🧪 When It Breaks: Resetting with Changing Initial State

Let’s say we do this:

function Parent() {
const [isDarkMode, setDarkMode] = useState(true)
return (
<>
<button onClick={() => setDarkMode(prev => !prev)}>Toggle Initial</button>
<Toggle initialOn={isDarkMode} />
</>
)
}

Now if the user toggles isDarkMode and then hits reset in the Toggle component…

❌ Without useRef: Reset will use the current isDarkMode, not the original.

✅ With useRef: Reset will go back to the original isDarkMode value.

This difference is subtle but can lead to serious bugs—especially when components are reused or toggled conditionally.


✅ Best Practices for the State Initializer Pattern

DoDon’t
Use useRef to lock in initial stateDon’t trust current initialX values after mount
Combine with useReducer for complex state logicAvoid repeating the same initialX logic in multiple places
Name your initializer and reset methods clearlyDon’t rely on useEffect to “patch” initialization

🧠 When to Use the State Initializer Pattern

Use this pattern whenever your component:

  • Accepts an “initial” value
  • Has a reset button or logic
  • Should not be influenced by future prop changes to the initial value

Some examples:

  • Form inputs that reset to defaults
  • Toggle switches that return to a default mode
  • Games or counters that reset to starting values

🔚 Conclusion

The State Initializer Pattern is simple but powerful. By using useRef, you can avoid subtle bugs and make your components more predictable—especially in dynamic or complex applications.

Reset behavior should feel intuitive. By anchoring to the original initial value, you ensure your users (and teammates) always know what to expect.


🧪 Want a Playground?

Want a CodeSandbox demo of this pattern in action? Just ask and I’ll hook you up with a live example! 🧑‍🔧


Happy resetting! 💡


Tags

#ReactPatterns

Share

Previous Article
Advanced React Patterns: Prop Collections and Getters

Table Of Contents

1
🏁 The Basic Hook
2
🎚️ Adding Initial State
3
🔄 What About Reset?
4
🧷 Locking in Initial State with Refs
5
🧪 Real Example: useToggle with Reducer
6
🧪 When It Breaks: Resetting with Changing Initial State
7
✅ Best Practices for the State Initializer Pattern
8
🧠 When to Use the State Initializer Pattern
9
🔚 Conclusion
10
🧪 Want a Playground?

Related Posts

Advanced React Patterns: Control Props
July 09, 2025
2 min
© 2025, All Rights Reserved.
Powered By

Quick Links

About Me

Legal Stuff

Social Media