From time to time, you create a React component as a function since state is not necessary at that point. In the end, you catch yourself converting your function into an ES6 class anyway. Unfortunately, classes often tend to become complex and make code reuse difficult. Thanks to a new feature proposal called Hooks this may become a matter of the past sooner than you think!
At the time of writing, React Hooks are only available in React 16.7 alpha. They aren't introducing any breaking changes and can easily be used side by side with stateful logic that you have implemented at other places in your application (you just can't use Hooks in ES6 classes). The feature proposal introduces several different types of Hooks (you can even build your own Hooks). This article focuses on two of those: State Hooks and Effect Hooks.
State Hook
Before Hooks were introduced, an app that would help Santa keep track of how many gifts he delivered would probably have looked like this:
class SantaApp extends React.Component {
constructor(props) {
super(props);
this.state = {
giftCount: 0
};
}
render() {
return (
<div>
<p>Ho ho ho! You have delivered {giftCount} gift(s).</p>
<button
onClick={() =>
this.setState({
giftCount: this.state.giftCount + 1
})
}
>
Gift delivered
</button>
</div>
);
}
}
We have an initial state { giftCount: 0 }
which can be changed by Santa
pressing a button that calls this.setState()
. When we want to display the gift
count, we use this.state.giftCount
.
Using a State Hook, the result becomes a lot more compact:
import { useState } from 'react';
function SantaApp() {
const [giftCount, setGiftCount] = useState(0);
return (
<div>
<p>Ho ho ho! You have delivered {giftCount} gifts.</p>
<button onClick={() => setGiftCount(giftCount + 1)}>
Gift delivered
</button>
</div>
);
}
First, we have to import the useState Hook. Then, the initial number of gifts
(which is zero) is passed to the Hook as a parameter. useState returns an array
containing the current state (which we called giftCount
in this example) and a
function (setGiftCount
) which triggers a re-render of the component and
updates the state. In addition to numbers, State Hooks work fine with strings,
booleans or objects as well. You can even define multiple Hooks in a single
component:
const [giftsDelivered, setGiftsDelivered] = useState(false);
const [nextGift, setNextGift] = useState([
{ title: 'Learn about React Hooks', type: 'book' }
]);
Why 'this' isn't so hard anymore
If you've read the third article of this Advent
calendar, you may recall that binding this
can be troublesome and that you probably have forgotten it several times. Since
there is no this
in function components (functions don't have
instances), we don't need to worry about it when using Hooks. Isn't that
awesome?!
Effect Hook
While State Hooks let you use stateful logic in function components, the Effect Hook makes it possible to perform side effects (such as fetching data from a server, changing the DOM, etc.). By default it runs after every render (both after the first render and after every update) and can therefore be used to replace the lifecycle methods componendDidMount and componentDidUpdate.
We can now extend our app to fetch the next gift to be delivered on every
re-render (e.g. when giftCount
changes):
import { useState, useEffect } from 'react';
function SantaApp() {
const [giftCount, setGiftCount] = useState(0);
const [nextGift, setNextGift] = useState([]);
useEffect(() => {
fetch('https://api.santaapp.no/nextGift')
.then(response => response.json())
.then(data => {
setNextGift(data);
});
});
return (
<div>
<p>Ho ho ho! You have delivered {giftCount} gifts.</p>
{nextGift.map(({ title, type }) => (
<p key={title}>
The next gift to be delivered is the {type} "{title}".
</p>
))}
<button onClick={() => setGiftCount(giftCount + 1)}>
Gift delivered
</button>
</div>
);
}
As you can see, we imported useEffect and added the Hook to our component which fetches information about the next gift and stores the value using a newly added State Hook.
Skipping Effects
Sometimes you don't want to trigger your Effect Hook on every re-render. This can be achieved by passing an array as a second argument to useEffect. Passing an empty array will only run the effect when your component is mounted and unmounted. If you pass an array containing some values, the effect is skipped as long as these values aren't changing.
useEffect(
() => {
// only re-run if giftCount changes
},
[giftCount]
);
How to continue from here?
Hooks are experimental and their implementation might change in the future. Therefore, you should wait to use them in any critical projects. Until then, you can try Hooks here, or start using them in your hobby project. Hope you got hooked on Hooks!