Chapter 01
React Evolution at a Glance (v15 → v18)
React's history at a glance — v15, v16 (Fiber), v17, v18 — what carried forward into 19.2, and what got deprecated.
Published 2026-05-23
🎯 Chapter Goal — After this chapter you can place any React API on a timeline (v15 / v16 / v17 / v18), recognise what carried forward into 19.2 and what didn’t, and stop confusing eras when reading legacy code or old blog posts.
🧭 Prerequisites — None. Skim before Ch 2.
🔹 1.1 React 15 era (2016)
The world before hooks. Components were class components, or stateless function components for read-only render.
// React 15-style
const Counter = React.createClass({
getInitialState() { return { count: 0 }; },
componentDidMount() { /* … */ },
render() {
return <div>{this.state.count}</div>;
},
mixins: [PureRenderMixin], // ← extinct
});
Things that existed in 15 and don’t anymore:
React.createClass— replaced by ES6 classes, then by function components.- Mixins — composition via shared methods. Replaced by HOCs, then render props, then hooks.
React.PropTypes— moved to its ownprop-typespackage.
Carried forward: the basic component / JSX / state / lifecycle mental model.
🔹 1.2 React 16 era (2017–2019) — Fiber
The under-the-hood rewrite. React’s reconciliation algorithm moved from synchronous recursion to Fiber — a cooperatively-scheduled, interruptible work loop. Same API for the user; entirely different runtime.
New APIs that landed in 16:
- ES6 class components (the
class extends React.Componentstyle) replacedcreateClass. - Error boundaries — class components with
componentDidCatch/getDerivedStateFromError. - Fragments —
<>…</>syntax; sibling elements without a wrapper. - Portals —
ReactDOM.createPortal(child, container); render into a different DOM subtree. - Hooks (16.8, Feb 2019) — the inflection point.
useState,useEffect, etc. Function components became first-class. forwardRef— pass refs through wrapper components.React.memo— function-component equivalent ofPureComponent.- Context (modern) —
React.createContextAPI replaced the oldchildContextTypessystem. Suspense(for lazy components) —React.lazy()+<Suspense fallback>.
Why hooks won so fast: they collapsed three separate concerns (state, lifecycle, code reuse) into one mental model. Class components still work, but new code is hooks.
// React 16.8+ idiom
const Counter = () => {
const [count, setCount] = useState(0);
useEffect(() => { /* mount */ return () => { /* unmount */ }; }, []);
return <div>{count}</div>;
};
🔹 1.3 React 17 era (2020) — the boring release
By design. Zero new developer-facing features. The point was:
- Gradual upgrades — 17 made it possible to embed React 17 in a React 16 page (and vice versa). Big-org migration support.
- New JSX transform —
import React from 'react'no longer required in every file. Bundler-level setting flipped this on. - Event delegation moved from
documentto the React root — small but consequential for embedding.
If you skipped 17 in real-time, you didn’t miss features. You missed groundwork for the next two majors.
🔹 1.4 React 18 era (2022–2024)
Concurrent rendering shipped for users in 18. Fiber had been there since 16; 18 exposed the user-facing primitives.
Headline APIs:
- Concurrent rendering — React can interrupt rendering work to handle higher-priority updates.
- Automatic batching —
setStatecalls in promises, timeouts, native handlers are batched. Previously only batched inside React event handlers. useTransition— mark a state update as non-urgent. UI stays responsive during heavy renders.useDeferredValue— debounce a value to be used in render.useId— stable IDs for SSR/hydration without collision.useSyncExternalStore— the bridge for external state libraries (Zustand, Redux). Removed an entire class of tearing bugs.useInsertionEffect— for CSS-in-JS libraries; fires before layout effects.- Suspense for data (with frameworks) — Suspense started being usable for async data, not just lazy components.
createRootreplacedReactDOM.render. Old API still works, gated to legacy mode.- Strict Mode double-invoke in dev — runs effects + state setters twice in dev to surface impurity bugs.
// React 18 idiom
const App = () => {
const [filter, setFilter] = useState('');
const [isPending, startTransition] = useTransition();
const onChange = (next: string) => {
setFilter(next); // urgent
startTransition(() => recomputeExpensive(next)); // non-urgent
};
return /* … */;
};
Strict Mode in 18 became actively useful. Effects firing twice surfaced bugs around cleanup, idempotence, and missing dependencies — bugs that were hiding under “it works in dev because dev runs once.”
🔹 1.5 What carried forward; what got deprecated
Carried forward into 19.2
- The whole hooks API (
useState,useEffect, etc.). - Strict Mode double-invoke.
createRoot,<Suspense>,useTransition,useDeferredValue,useId,useSyncExternalStore.- Fragments, portals, error boundaries.
- The new JSX transform (
import Reactis optional). React.memo(still works; usually unnecessary after the compiler — Ch 2 §2.1).- Context (still works; carefully — Ch 13 §13.6).
Deprecated / removed by 19.2
React.createClass(gone in 16, didn’t survive past then).- Mixins (replaced by hooks;
PureRenderMixinetc. are extinct). - String refs (
ref="foo"); use callback refs oruseRef. componentWillMount,componentWillUpdate,componentWillReceiveProps— removed (UNSAFE_ aliases linger for backward compat).findDOMNode— gone in 19.- Default export of
ChildrenAPI; helper utilities discouraged. forwardReffor new components — refs are props in 19 (Ch 2 §2.7).react-helmet-asyncfor metadata — native in 19.2 (Ch 2 §2.4).
The big mental shift across the eras
React 15: classes + mixins; sync rendering
React 16: classes (or fn) + hooks; sync from outside, Fiber under the hood
React 17: no new surface; gradual-upgrade plumbing
React 18: function components + hooks; concurrent rendering exposed
React 19: function components + hooks + compiler; refs are props; native metadata
If you learned React after 16.8 and skipped the classes era, you saved no time — but you also miss the why behind some current APIs (the “why does memo even exist?” question makes more sense if you’ve seen shouldComponentUpdate).
🪤 Common Pitfalls (in legacy migration)
- Treating class components as urgent removals — they still work; migrate when it’s natural.
- Translating lifecycle methods 1:1 to
useEffect— sometimes the right answer is a different pattern (data layer, store). - Keeping
forwardRefafter migrating to 19 — refs are props now (Ch 2 §2.7). - Removing
<StrictMode>because “it makes effects fire twice” — fix the effects. - Trusting blog posts without an era marker — patterns from 2019 may not apply.
✅ Recap
- React 15 → 16 → 17 → 18 → 19.2: each era has identifying APIs.
- Hooks (16.8) was the inflection point.
- 17 was about plumbing, not features.
- 18 exposed concurrent rendering and the modern data-fetching story.
- 19 closes the loop: compiler, native metadata, refs as props.
🔗 Further Reading
- React blog archive — each major version’s announcement post.
- Dan Abramov — “Things I don’t know as of 2018” / “Before You memo()” / various era-pieces.
- Ch 2 — what’s new in 19.2 (the destination of this whole evolution).
In the book — not on the site
Each topic has an 🧠 Under-the-hood subsection — the algorithm, the data structures, what React DevTools surfaces, debugging recipes. Plus a 🧪 hands-on lab per chapter with a starter repo. Reserved for the book.