After the React team announced the proposal of a new feature called Hooks at React Conf 18, there was a strange reaction in the React community: a flurry of tutorials describing how to use it, as if it was already in React core.
What is explicitly described as “a new feature proposal” was treated by some as a feature announcement. Clearly, there’s too much hype and “let’s rewrite everything right now” attitude, as Dan Abramov of the React team remarked:
So let’s take a chill pill, not even bother with the proposed API, and ponder how React got to this point.
If you’d rather see the code and refactor your app to use Hooks right now, the official docs are an excellent starting point.
In one of the first conference talks about React, Pete Hunt uses the word hooks to refer to methods of
React.Component, which allow the user to provide custom update logic — to hook into React’s internals and tweak their behaviour. The new feature proposal is introducing hooks in a similar sense — as a way of interacting with React by hooking your code into the React’s engine.
This idea of using functions instead of inheriting from classes plays well with the functional and declarative spirit of React. Because class-based components suffer from the old banana-gorilla-jungle problem, as described by Joe Armstrong:
the problem with object-oriented languages is they’ve got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle.
React.Component, very often all you need is state or a ref, but what you get is a collection of methods you’ll never use and the necessity of writing the dreaded
this keyword (which is an anagram for you know what). Basically, Hooks are a proposal to ditch the classes and instead of inheriting from React, hook into React.
How to React without
The class-based API of React has been a problem for some time now. The ES6 classes themselves are not really classes (just syntactic sugar masking prototypal inheritance), they don’t compose well, and the usage of
this keyword creates binding issues, especially when performing asynchronous operations.
So the search for class-less React development was on. Maybe the most notable project — now discontinued because of the introduction of Hooks — is
recompose (the first example in the docs is providing a state to a function component). However,
recompose is making heavy use of the Higher Order Component pattern, which unfortunately creates a hard to read and false hierarchy in the render tree.
Another example is Reactions
Component, which uses another very popular pattern for making the class components more composable — Render Prop. The problem here is that a user first has to be pretty familiar with class-based React API to make some sense of how Reactions
Component is to be used.
There are also cool projects that use the new ES6 Proxies to manage state, like
react-easy-state or — in a more lightweight and experimental way —
react-recollect. I recommend the article about the latter, which is a nice dive into how a super-readable and self-explanatory state management can be achieved. Sadly, the support for ES6 Proxies is far from ideal.
Currently, the Hooks implementation allows for replacing almost all of class-based component’s functionality (Hook equivalents for
getDerivedStateFromErrorare to be added soon), so after they’re added to React, the search can be called off. Classes in React aren’t going anywhere anytime soon, but it’s clear that React team envisions a class-free future for the library.
Just one kind of component, please
The dichotomy between class and function components is an unnecessary burden for a user. I tend to regard a function component as a kind of lightweight little brother of the full-fledged class component — also, the distinction plays nicely with the presentational and container component pattern. But when I imagine learning React now, I think it’d be a bummer that a decision about using state or not (the most basic feature of React) has to be made before even starting to write the component.
It’s a bit like synchronous vs. asynchronous functions:
Promises help us escape callback hell, but why should a programmer have to care if an operation is async or not in the first place? For me at least, Hooks are a much more understandable abstraction than
class Something extends React.Component— how do I know what’s lurking in the jungle that I have to bring along with the
Even more declarative and functional future
React prides itself on being declarative, but using lifecycle methods is quite imperative — a classic example is duplicated code in
componentDidUpdate methods. Using Hooks, that can be achieved with
useEffect function (there’s a great example of how Hook’s declarative API beats lifecycle methods’ imperativeness in the official documentation).
With Stateless Functional Components, React moved more in the direction of functional programming. Hooks are a move even further in that territory, as they enable building full-featured React apps without the use of classes, but with functions only.
When the proposal is finalized, it will be the biggest change to how React apps are developed since the launch of the library. Today’s nifty patterns like Higher Order Components or Render Prop will seem quirky or even hacky (because they introduce a false hierarchy). The familiar
class App extends React.Component with its list of lifecycle methods will be a thing of the past, and if it wasn’t for JSX syntax, a piece of code might not even look like a React component at a first glance.
But until then, let’s not get too attached to the proposed API and focus on writing code using cool, stable features.
Plug: LogRocket, a DVR for web apps
LogRocket is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.