Skip useEffect Hook on the First Render
May 15, 2020
This is the second article in Replacing the Lifecycle Methods with useEffect Hook series. Read the first article here.
Consider this class component:
class CountInTitle extends React.Component {state = {count: 0,}componentDidUpdate(prevProps, prevState) {if (prevState.count !== this.state.count) {document.title = `Current count is ${this.state.count}`}}render() {return (<buttononClick={() =>this.setState((prevState) => ({ count: prevState.count + 1 }))}>Increment</button>)}}
It’s a simple component that updates the title of a document whenever you click on an Increment button. Maybe you didn’t catch it (kudos if you did), but nothing happens on the mount. Users will see the count only after the first click. Now let’s try to achieve this using the functional component and useEffect
hook. For simplicity’s sake, I’ll only show the code inside the useEfect
. We will also need to replace a class with the function, the rest remains unchanged.
useEffect(() => {document.title = `Current count is ${this.state.count}`}, [count])
It still works, but with one issue. It will display “Current count is 0” in the title of a document as soon as the app loads. useEffect
will apply an effect on the initial render, and the title of the document will be updated. This is expected behavior, but depending on your use case, this may not be something you desire. So how to prevent useEffect
from running on the initial render?
The most straightforward way is by using a boolean flag that will tell the useEffect
if it’s initial render or not. We will be using useRef
to achieve this.
const notInitialRender = useRef(false)useEffect(() => {if (notInitialRender.current) {document.title = `Current count is ${count}`} else {notInitialRender.current = true}}, [count])
Let’s break it down line by line. We create a new ref notInitialRender
and set it to false. In the useEffect
hook, we ask if the current value of the notInitialRender
is true. Since it’s false on the first render, we will end up in the else side and set the current value of the ref to true. Now that the current value of the notInitialRender
is true, every subsequent change of count
will update the document title.
We could also use the value in the state instead of the ref, but that would result in an unnecessary component update. You should not keep something in the state if you don’t use it for rendering. This is the perfect place for useRef
.