React’s new Hooks API allows us to finally share behaviour without any of the intrinsic problems that have plagued the community previously
I have been developing with React since it’s early days and during that time there have been many attempts by both influencers, as well as the core team to improve the API and patterns developers are using to creating software. One of the biggest challenges we have had was how to share behaviour neatly between components to enable reuse or even just separation of concerns. Every single solution proposed up until this point had some problems associated with it.
Luckily React has just released a glimpse of their new API for sharing behaviour in React components that looks like it will solve many of the problems we have had in the past.
This is what it looks like:
First lets have a little look at how we got here…
When React was first released, classes were not available in ES5 so React shipped with it’s own class creation method, which included the ability to merge in a bunch of methods from an object into the component you create.
This unfortunately led to similar problems you find with classical inheritance; mainly indirection from magic undocumented methods appearing out of nowhere and being used on components. The developer has no idea what functionality is available to them and more importantly what is not.
Higher order components (or HOCs) are an attempt at applying the Functional Programming concept of higher order functions to React components. The idea is that you alter your component by wrapping it in an outer component that provides behaviour, composing the original component and passing the behaviour’s results as new props to the original component. This is done in a similar fashion to the way higher order functions pass data via closures.
What’s great about higher order components is that you can see the data coming into the component as a prop. It is no longer magical like with Mixins.
However there are issues. The main problems with higher order components include:
- They are complex to setup.
- You can not distinguish between data that was coming from the HOC and the data that was passed to the component.
- The HOC is external to the component yet the component remains dependent on the HOC. Remove the HOC and the component would not always work if it depends on the HOCs data.
- You can end up with huge render trees as behaviour components contain render components.
Render props are a relatively new trend and offer an an answer to some of the dependency and indirection problems that HOCs can cause. They are created by enabling a component to accepts a function prop that it will use to render it’s children. This allows the component to provide a closure for it’s children as well as some behaviour and new data.
However they can be abused. See this (event though contrived, unfortunately rather typical) Apollo React example:
If you have ever worked with me you probably know my hesitation around using render props. I think it can be a useful pattern in certain contexts however it has a few major issues:
- It declares false hierarchies ie. pyramid of doom.
- Encourages passing inline functions to child components which if not checked can lead to performance problems.
- Create confusing closure structures which should actually be inline.
- Leads to very verbose component JSX
Since it’s inception React has included various lifecycle methods for developers to hang code on based on particular execution times within the lifecycle of the component being rendered in React. Being able to support this asynchronous behaviour is why React components were modelled as classes to begin with. This model is simple and it provides an intuitive way to attach behavioural code to a Component.
this object which means you need to understand and take care of binding your handlers as you pass them around to child components.
React 14 introduced stateless functional components to resolve this but they did not provide ways to access lifeCycle methods which relegated them to only be used on components that would not grow into requiring complex behaviour
Two days ago at ReactConf 2018, the React team announced their new hooks API. React’s new API attempts to solve these problems by making HOCs and render props obsolete. The new API allows for true state driven behaviour sharing while also:
- Providing a way to get access to state managed props and be able to easily follow exactly where that state has come from.
- Returning memoized functions by default avoiding performance penalty from downstream PureComponents
- Not creating a pyramid of doom
- Not touching props. What you pass into the component in JSX is what you get in props.
- Not creating any magic behaviour methods.
- Leading to simpler JSX that is more concerned with component rendering and less with behaviour.
- Removing the performance overhead of wrapping components in layers.
- Allowing for custom behaviours to be bundled off into their own functions that can be exported by libraries.
Here is an example of how to create a custom hook:
The main drawback to using the hooks API is that all the “hooks” methods must be run in the same order every time the component is rendered.
In fact when I first heard about this I was a little concerned. I don’t like the idea of hidden rules I have to follow. I think it means that new folks coming to the API will struggle to understand why their code is not working.