Component Apis Before React Hooks Settled

Hero image for Component Apis Before React Hooks Settled. Image by Gabriel Vasiliu.
Hero image for 'Component Apis Before React Hooks Settled.' Image by Gabriel Vasiliu.

React component APIs were not always hookshaped.

Before hooks became the default way many teams shared stateful logic, React codebases leaned on class components, higherorder 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

Higherorder 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, overmemoisation, and effects used for work that belongs elsewhere.

Every era has its footguns.


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 frontend components and accessibility in reusable frontend 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, higherorder 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.

Planning a platform change?

I help teams make difficult platform work clearer, from architecture decisions and migrations to launch recovery, performance, and search visibility.