React v16.8 is meant for solving some problems that we’ve been dealing with for so long:
- Confusing classes for both human and machines: Classes require a lot of boilerplate, force us to bind event handlers and to understand how
- Reusing logic: React doesn’t offer a way to “attach” reusable behavior to a component. Mixins was the first workaround but we ended up concluding that mixins comes with worse problems that the one we were trying to solve. Patterns like render props and HOCs solve this problem but they force us to restructure our components by surrounding them with several abstractions, which leads to the “wrapper hell”.
- Giant components: The more a component grows, the more we have to spread its logic and side effects through the whole component itself. This happens mainly for two reasons: lifecycle methods containing a mix of unrelated logic and the impossibility to split these components into smaller ones because the stateful logic cannot be extracted.
The thing is that if we approach each problem in an isolated manner, it seems like solving one of them makes some other worst. For example trying to extract logic from a giant component leads to the wrapper hell and viceversa.
From now, we can consider the three problems described above not as isolated problems but as symptoms of the same problem: React doesn’t provide a stateful primitive simpler than a class component.
Hooks are the new feature proposal that solves the problem:
- Reusing logic: With Hooks, you can extract stateful logic from a component and reuse it without changing your component hierarchy.
- Giant components: Hooks let you split one component into smaller functions based on what pieces are logically related, rather than forcing a split based on lifecycle methods.
- Confusing classes: Hooks let you use React’s features without classes.
Hooks are also completely opt-in and 100% backwards-compatible so we can migrate our projects and start using them seamlessly! yay!
The State Hook: useState
Below there is a simple counter implemented without using hooks:
And below is the same component but refactored to use the useState hook:
So the state hook allows us to declare a “state variable” which will be preserved between renders by React.
useState accepts only one argument, the initial value (value for the state variable in the initial render), and returns a pair of values: the current value and the updater function.
How does the updater function
setState work? Much the same as the “old one”, but the new
setState will replace the whole state with the argument passed instead of merging it with the old state.
So how would we store our state objects? We can still do the merge manually in the updater function, but it won’t be necessary in most cases since we can use as many state variables as we want in the same component:
Hooks can be easily extracted and reused:
If a hook is just a function… Can it be extracted from the component? YES! 🙆🏼
This way we’ve just extracted logic which uses internal state of the component, from the component itself.
The Effect Hook: useEffect
The Effect Hook lets you perform side effects in functional components.
If we wanted to show the counter value as the title of the page in the example above, we would have to do something like this without hooks:
The same example using hooks:
By using this Hook, you tell React that your component needs to do something after render, so React will call the effect after performing the DOM updates.
By default, React runs the effect after every render including the first one, but we can tell React to skip applying it if certain values haven’t changed between re-renders. To do so, we only have to pass an array as an optional second argument to
In some cases we need our component to be subscribed to the changes in some external source. We normally do this by subscribing it to the source on
componentDidMount and unsubscribing it on
How can we do this with hooks?
useEffect function can optionally return a function which is the cleanup mechanism for effects. React performs the cleanup when the component unmounts and before applying the next effects.
Pay attention to the second argument of the
useEffect function in the example above. If we don’t define the second argument, the effect will be called after every re-render. To avoid so in the example, we passed an empty array as the second argument which will presumable not change over the re-renders so the effect will only be called after the first render and cleaned up when the component unmounts.
This way, we can think of
useEffect Hook as
componentWillUnmount combined. Since we can use as many effects as we need, the code can be now arranged by concern instead of spread between the different lifecycle methods. I found this gif in twitter, which shows clearly the benefits of React Hooks, thanks to @prchdk:
Some rules with Hooks
- Only call hooks at the top level: Don’t call Hooks inside loops, conditions or nested functions.
use” and that may call other Hooks. We define custom Hooks to extract and share logic between components.
In case you didn’t notice, we’ve already wrote one in the previous section “Hooks can be easily extracted and reused”:
Since Hooks are completely opt-in and 100% backwards-compatible, we’re already trying them in some test projects. We’ll write about our thoughts soon so stay tuned!