The React Reactivity Model: Mastering Derivations, Effects and State Synchronization
17 April 20254 min read
In the world of React, reactivity often feels like a second-class citizen. We rely on useEffect
for side effects, useMemo
for optimization, and useState
for everything else—but how do these pieces actually work together? What happens when state changes ripple through a component tree? And more importantly, how can we write reactive logic that's both correct and efficient?
In this deep dive, we’ll explore the fundamentals of reactive systems—derivations, effects, and synchronization—through the lens of React. Inspired by concepts from frameworks like SolidJS and MobX, we’ll reframe these ideas using only idiomatic React, helping you understand why and when things re-render, and how to write better, cleaner state-driven components.
🧠 Derivations vs Effects in React
Let’s start with two key reactive patterns:
✳️ Derivations with useMemo
A derivation is a value calculated from other state. In React, you use useMemo
:
This is a pure transformation. It never triggers side effects or updates state—it just reacts to inputs.
🔁 Synchronization with useEffect
By contrast, this is synchronization logic:
This updates one state based on another, but it risks timing bugs and unnecessary renders. Derived values should ideally be expressed with useMemo
.
⚠️ Why Derivations Are Safer
React’s useEffect
runs after render, so updating state inside effects may cause double renders, stale values, or UI flickers.
Using useMemo
instead of setting derived state with useEffect
avoids these issues and keeps logic pure and consistent.
🕸️ Deriving from Multiple Values
Let’s build a dependency graph in React. We’ll mimic this structure:
🔍 What We’ll Build
a
: source stateb
,c
: derived froma
d
: derived fromc
e
: derived fromb
andd
All derivations are done with useMemo
.
✅ Full React Example (No Signals)
✅ What This Code Demonstrates
b
,c
,d
,e
are all derived froma
— purely withuseMemo
- No unnecessary
useEffect
logic for state syncing - React’s dependency tracking ensures correct evaluation order
🔀 Pull-Based Reactivity in React
React is pull-based. Components pull data when they render. This works well for derivations via useMemo
, but React’s useEffect
is asynchronous, which can be dangerous for direct state-to-state syncs.
Instead of:
Prefer:
This avoids redundant updates and lets React optimize.
💡 Summary
- ✅ Prefer
useMemo
for derived values - ❌ Avoid
useEffect
to sync state unless necessary - ⚙️ React is pull-based, and benefits from pure derivation logic
Final Thoughts
Reactivity isn't just about updating the UI—it’s about maintaining consistency, optimizing performance, and ensuring predictable behavior across your components. While React’s approach to reactivity differs from fine-grained systems like Solid or Svelte, understanding the underlying principles—derivations, synchronization, and the flow of updates—empowers you to make better architectural decisions.
By modeling derived values properly, avoiding unnecessary effects, and respecting React’s render cycle, you can write code that’s not only cleaner but also more resilient. The more you understand how reactive data flows—from signals to state, from memoized values to effects—the more you'll be able to harness React’s power effectively.
This was just the beginning. In the next part, we’ll dig into lazy vs eager computations, and how you can use techniques like memoization and selector composition to build even more optimized reactive trees in React.
Stay tuned—and keep your components consistent.