Component Apis Before React Hooks Settled

React component APIs were not always hook‑shaped.
Before hooks became the default way many teams shared stateful logic, React codebases leaned on class components, higher‑order components, render props, container components, and context patterns. Some of that code now looks awkward. Much of it was entirely reasonable for its time.
The useful retrospective is not "old React was bad". It is understanding which patterns aged well, which created friction, and why hooks changed the default.
Class Components Carried Several Responsibilities
Class components bundled state, lifecycle methods, instance methods, refs, and rendering into one structure.
That worked, but it could make related logic spread across methods:
- setup in
componentDidMount - updates in
componentDidUpdate - cleanup in
componentWillUnmount - rendering elsewhere
For simple components this was fine. For complex behaviour, lifecycle coordination became hard to read.
Higher‑Order Components Shared Behaviour through Wrapping
Higher‑order components were a common way to share logic.
They could inject props, connect to stores, add tracking, handle permissions, or provide data. The pattern was powerful, but stacking wrappers made component trees harder to inspect and types harder to follow.
The API cost was often hidden in composition:
- where did this prop come from?
- which wrapper owns this behaviour?
- which wrapper order matters?
- how do refs pass through?
Those questions became familiar in larger codebases.
Render Props Made Flow Explicit
Render props offered a more explicit way to share stateful behaviour.
A component owned the behaviour and called a render function with the values needed by the UI. That made data flow visible, but it also created nesting and noisy JSX when several behaviours were composed.
Render props were a good pattern for many cases. They just did not scale elegantly as the default answer to every shared concern.
Context Became More Practical Over Time
Context existed before hooks, but hooks made consuming context feel much more natural.
Before useContext, context often involved consumers and render functions. That was workable, but verbose. Hooks gave teams a simpler way to read context inside function components.
The article on the React Context API covers the earlier version of that pattern.
Hooks Changed Logic Sharing
Hooks made it easier to extract behaviour without changing component hierarchy.
The official React hooks reference shows how broad that model has become: state, effects, refs, context, memoisation, transitions, and custom hooks all fit into the same mental model.
That consistency is useful. It also created new failure modes: dependency arrays, stale closures, over‑memoisation, and effects used for work that belongs elsewhere.
Every era has its foot‑guns.
Migration Should Be Selective
Not every class component or render prop needs to be rewritten because hooks exist.
Good reasons to refactor include:
- the component is actively changing
- shared logic is duplicated
- lifecycle behaviour is hard to reason about
- tests are difficult because of the old shape
- the old API blocks useful composition
Weak reasons include novelty, tidiness, or wanting every file to look the same overnight.
Those choices become easier to spot in an isolated component environment. Using Storybook to document front‑end components and accessibility in reusable front‑end components both depend on clear component state and props.
Wrapping Up
React hooks settled a lot of component API debates by giving teams a clearer default for stateful logic.
But the patterns before hooks were not mistakes. They were responses to the constraints of the time. Understanding them helps when maintaining older React code, planning migrations, and deciding which abstractions are worth keeping.
Key Takeaways
- Class components, higher‑order components, render props, and context all solved real problems before hooks.
- Older patterns often became awkward through composition and lifecycle complexity.
- Hooks changed the default for sharing stateful logic without adding wrapper components.
- Hooks introduced their own risks, especially around effects and dependencies.
- Refactor old component APIs selectively, based on change risk and value.