The Component LifeCycles
Prerequisites
- React
- Components
- State and props
Learning Objectives
After this lesson you will be able to:
- Explain the three 3️⃣ phases of the Component lifecycle
- Run code within each of the three 3️⃣ phases
- Use
useEffectto implement the three 3️⃣ main lifecycle methods
Framing
So far we've learned that React Components can be used to break down the UI into smaller and smaller bit of reusable functionality. The Component model even encourages going as low level as rendering a single button.
Part of the Component reusability design is that it allows them to contain as much logic as needed to adapt to their environment and implementation. That flexibility might require the Component to perform some action when it's first mounted, then updated and perhaps, even when it's unmounted.
Take for instance AirBnB. When you perform a search, the site returns a set of data points that match the query and must update state in some way in order to render those items as Components.
The Components themselves may need to keep track of if, and when, it's been added as a favorite ❤️ or adjust it's own layout based on mobile or tablet layout.

⏰ Activity - 5min
Let's take a look at the following versions of the Mars website that is built in React.
Let's do the following together:
- Open both sites in separate tabs and with DevTools open as well.
- In DevTools use the
Toggle Device Toolbarto view the site in Responsive mode - Enable
Tabletview for both web sites - Select and examine the following element:
<div id="nav_container">...</div>
The Instructor will now direct your attention to those elements the difference between the two apps.
Note: The implementation of these two sites were created for the following medium article on 3-ways-to-implement-responsive-design-in-your-react-app
The 3 Main Phases Of The Component Lifecycle
As the complexity of the Component grows we can make use of more and Hooks to meet the demands of our business logic. Certain functionality might need to be performed only once when Component loads or with each re-render.
Below are the 3 phases that a Component goes through during it's lifetime.
- Mounting
- Updating
- Unmounting
Mounting and Unmounting occur only once during the lifecycle of the Component with Updating occurring as often as the Component is re-rendered.
Each phase can call a specific lifecycle method that runs as the last function call in that cycle.
Here is a visual diagram of the phases and their corresponding lifecycle methods.

Although up to this point we've only worked with Functional Components, in lue of Classes, however under the hood React is indeed initializing and calling them as if they were written as classes. The diagram represents the Components referenced as classes.
Mounting: called when a component is created and inserted into the DOM.
- constructor - initializes the component and sets state via
useState render()-returnsthe UI- DOM and Refs - done using
useRef - componentDidMount() - performed using
useEffect(() => {}, [])
Updating: usually triggered by changes in props or state.
- setState() - updates state which triggers the re-render
render()-returnsthe UI- DOM and Refs - done using
useRef - componentDidUpdate() - performed using
useEffect(() => {})(No [] included) - componentDidUpdate() performed using
useEffect(() => {}, [someValueToMonitor])
Unmounting: called when a component is being removed from the DOM.
- componentDidUnmount() -
useEffect(() => {})(No [] included)
The useEffect Hook
Now it's time to add yet another Hook to our collection, the useEffect Hook. This Hook lets us perform side effects in functional components.
A side effect is any application state change that is observable outside the called function other than its return value.
Several examples of side effects are:
- Logging to the console
- Making an API call
- Calling setInterval/setTimeout
As we pointed out during the overview of the React lifecycle, useEffect and be run in one of 3 ways:
useEffect(() => {}, [])- empty [] means this will only run once when the Component mountsuseEffect(() => {})- no [] means this will run on every render/re-renderuseEffect(() => {}, [someValueToMonitor])- run on mount and then only if the value has changed
⏰ Activity - 1min
Starter Code
Here is the starter code we will be working with: useEffect CodeSandbox Starter
https://codesandbox.io/s/useeffect-starter-9cpw0?file=/src/Counter.js
👍 - Click on the thumbs up when you have forked the starter code.
Mounting
Since every Component requires an initial mounting it makes sense that we start with the mounting phase.
Let's take a look at the Solution we are looking to implement. As we can see the app does the following:
- timer starts counting when the Component mounts
- timer increments every second
- clicking on the
pausebutton pauses the timer - clicking on
startre-initiates the counting sequence.
It's safe to say that our current knowledge of React will fall short to implement that functionality and that we will need to make use of the useEffect Hook to implement the logic.
But let's first push the envelope and see how far we can go without useEffect before we hit our first wall.
Base Functionality
As of right now our implementation only contains the console logs needed to confirm that the buttons work.
Increment The Counter On Mount
Let's first see if we could at least call startTimer when the component is loaded. That's easy to do as we only need to call the function. For now let's put it just above the return statement.
startTimer();
return (
// ...rest of code
)
So it seems calling startTimer() when the Component loads does indeed execute the function.
Now let's add the logic startTimer that will increment the counter just once when it's mounted.
const startTimer = () => {
console.log("startTimer");
setCounter(counter + 1);
};
React doesn't like that very much and errors out as it takes us to brink of an infinite loop.
1 of 2 errors on the page
Error
Too many re-renders. React limits the number of renders to prevent an infinite loop
note
Note: Make sure to delete calling startTimer().
❓ - Why the error?
Answer
Calling startTimer() updates state which triggers the Component to update, which then calls the function again triggering another update..and so on..
ComponentDidMount - Run Once On Mount
So we need a means of calling startTimer() when the Component is initially mounted and not on any subsequent re-renders. For that use case we can use:
useEffect(() => {}, [])
Since useEffect is a hook it needs to be imported.
import React, { useState, useEffect } from 'react';
useEffect takes in a callback function as it's first param and an empty [] as its second. The empty [] is how we tell useEffect to run only on the initial mount.
useEffect(() => {
console.log("useEffect");
startTimer();
}, []);
Ok. So now we have a means of calling it a single time. That's enough to to get us started and now we can add the additional logic setInterval logic to increment every second.
This also bring us to the following best practice:
⭐ - Use the callback function version of useState if you need to reference the previous version of state.
Since setCounter is being called in the callback function of setInterval this is a perfect use case for using the callback version of setCounter
const startTimer = () => {
console.log("startTimer");
interval = setInterval(() => {
console.log("setInterval");
setCounter((prevState) => prevState + 1);
}, 2000);
};
const pauseTimer = () => {
console.log("stopTimer");
clearInterval(interval);
};
This seems to do the trick and were back on track with the Counter app. Now let's work out the logic to pause the timer.
Pausing The Timer
With 2 of the app requirements in working order let's see if we can pause the timer. Clicking on Pause Timer does indeed execute the function, as seen in the console logs, but does nothing to stop the timer.
If we also click on Start Timer a few times in a row we will see that it increments several times and begins to jump ahead.
ComponentWillUnmount
One thing that hasn't been mentioned yet is that every time a re-render happens, we schedule a new effect replacing the previous effect. Although in our use case useEffect isn't being called there is still a need to clear the interval just before the re-render. This falls ito the category of ComponetWillUnmount
React must clean up the previous effect before applying the next effect. In our case we need to remove the previous instance of the setTimer and create a new instance.
For this we need to refactor useEffect to do the following:
- im must clear the previous
setIntervalbefore the new instance ofuseEffectis called
To do this we add a final return statement that is passed a callback function.
useEffect(() => {
console.log("useEffect");
startTimer();
return () => clearInterval(interval);
}, []);
ComponentDidUpdate
This doesn't seem to do the trick. Thats because we need to run useEffect on every re-render or when the ComponentDidUpdate. In order to do that we remove the [].
useEffect(() => {
console.log("useEffect");
startTimer();
return () => clearInterval(interval);
});
ClearInterval Once More
Even with this implementation were still faced with the same problem that multiple clicks to Start Timer will cause the timer count much faster.
In order to resolve this we need to add one more clearInterval to startTimer to clear out any timer that was initiated before the next interval value.
const startTimer = () => {
clearInterval(interval);
//...rest of code
};
Here is good summary for implementing useEffect.
Once (similar to componentDidMount)
useEffect(() => {
// put 'run once' code here
}, []); // pass empty array
On State Change (similar to componentDidUpdate)
const YourComponent = () => {
const [state, setState] = useState();
useEffect(() => {
// code to run when state changes
}, [state]); // include all state vars to watch
}
On Props Change (similar to componentDidUpdate)
const YourComponent = ({ someProp }) => {
useEffect(() => {
// code to run when someProp changes
}, [someProp]); // include all monitored props
}
On Unmount (similar to componentWillMount)
useEffect(() => {
// return the cleanup function
return () => {
// put unmount code here
}
});
After every render (similar to componentDidUpdate)
useEffect(() => {
// put 'every update' code here
}); // no second argument
Bonus - Conditional Rendering A Component
A much better UI for our Counter would be to show only 1 button at a time. We essentially would toggle between the buttons with only one of them being visible at any one time.
Enabling this functionality would also require a bit of refactoring of our code which is beyond the scope of this bonus so we will only focus on how to conditionality render the buttons.
More State
First let's add an additional state value.
const [toggle, setToggle] = useState(false);
Ternary Operator
Now we can add the conditional logic needed to toggle between the buttons. We will do this using a ternary operator.
return (
<>
<div>Counter: {counter}</div>
{toggle ? (
<button onClick={startTimer}>Start Timer</button>
) : (
<button onClick={pauseTimer}>Pause Timer</button>
)}
</>
);
Complete Refactor
Here is the code that includes the complete refactor.