Skip to content

Commit 5db9783

Browse files
committed
Cleanup work using componentWillUnmount and useEffect() hook
1 parent fe890d2 commit 5db9783

4 files changed

Lines changed: 98 additions & 125 deletions

File tree

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,6 @@ In this repository, I've implemented (or implementing) all the concepts related
8080
2. When `state` changes: [Commit Details](https://github.com/Ch-sriram/react/commit/d69afffa5c2ca4dec8f915f966c22a8d59ff6066)
8181
6. `useEffect()` and its uses
8282
1. Usage inside a functional component: [Commit Details](https://github.com/Ch-sriram/react/commit/2c3f31c936f0fa3185ba6c7dffff1225b33d0d0b)
83-
2. Controlling the Behaviour of `useEffect()`: [Commit Details]()
83+
2. Controlling the Behaviour of `useEffect()`: [Commit Details](https://github.com/Ch-sriram/react/commit/fe890d24d62d9948acc5807c5ca69a5017a94189)
84+
7. Lifecycle Methods for Cleanup Work
85+
1. In class-based components using `componentWillUnmount()` & in functional components using `useEffect()`: [Commit Details]()

react-examples/src/components/Cockpit/Cockpit.component.jsx

Lines changed: 44 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -2,71 +2,62 @@ import React, { useEffect } from 'react';
22
import CockpitStyleClasses from './Cockpit-style.module.css';
33

44
/**
5-
* useEffect() in its previous implementation where we simply
6-
* logged to the console, ran every time - the Cockpit
7-
* component was rendered because of changes in the state/props
8-
* of the parent component.
9-
*
10-
* Now, what if we want to send an HttpRequest inside the
11-
* useEffect() lifecycle method, but only want to do it when
12-
* the component is rendered for the first time, and not for
13-
* every re-render cycle.
5+
* Now, if we are using useEffect() hook to handle the
6+
* functionality of componentWillUnmount() class-based
7+
* lifecycle method in a functional component, in that case,
8+
* in our useEffect() lifecycle hook, we can always return
9+
* either nothing, or, we can return an anonymous function,
10+
* which runs (and this is important) BEFORE whatever is there
11+
* in the useEffect(), but this behaviour of running before
12+
* the useEffect() hook, is only in-effect AFTER the (first)
13+
* render cycle only.
1414
*/
1515

1616
const Cockpit = props => {
17-
18-
// useEffect(() => {
19-
// console.log("[Cockpit.jsx] useEffect");
20-
// // Dummy HttpRequest using setTimeout()
21-
// setTimeout(() => {
22-
// alert("Saved data to cloud!"); // executes after 1000ms
23-
// }, 1000);
24-
// });
17+
useEffect(() => {
18+
console.log("[Cockpit.jsx] useEffect");
19+
// Dummy HttpRequest using setTimeout()
20+
const timer = setTimeout(() => {
21+
alert("Saved data to cloud!"); // executes after 1s
22+
}, 1000);
23+
return () => {
24+
// whenever this cleanup code is executed, we don't
25+
// want the alert to showup, and so, we simply clear
26+
// the timeout as shown below.
27+
clearTimeout(timer);
28+
console.log("[Cockpit.js] cleanup work in useEffect");
29+
}
30+
}, []);
2531

2632
/**
27-
* The above useEffect() executes after every re-render, and
28-
* that's a hassle, because we only want the useEffect() hook
29-
* for this component run, only when there's some change
30-
* to the props we receive for this component.
33+
* Even with the useEffect() hook defined with the cleanup
34+
* code being returned, we'll never see that cleanup code
35+
* being executed, until the respective component actually
36+
* is removed from the view.
3137
*
32-
* Therefore, if we only trigger useEffect() method when our
33-
* this.state.persons changes in <App/> component (which is
34-
* referred as props.persons in here), then for that, we will
35-
* send in a second argument to the useEffect() lifecycle
36-
* hook, which is some array of data that can change, and
37-
* when that data changes, depending on the change, the
38-
* useeEffect() lifecycle hook executes.
39-
*
40-
* We can see its implementation below
38+
* In this case, the Cockpit component has to be removed from
39+
* App.js component (parent component), onClick of some
40+
* button and so on... (which is handled in App.js)
4141
*/
4242

43-
// useEffect(() => {
44-
// console.log("[Cockpit.jsx] useEffect");
45-
// // Dummy HttpRequest using setTimeout()
46-
// setTimeout(() => {
47-
// alert("Saved data to cloud!"); // executes after 1000ms
48-
// }, 1000);
49-
// }, [props.persons]);
50-
5143
/**
52-
* Now, if we want to execute the useEffect() hook only when
53-
* the component renders the first time, in that case, we
54-
* would send in an empty array [] as the 2nd argument to
55-
* the useEffect() lifecycle hook. Here, we are trying to
56-
* augment componentDidMount()'s usage using useEffect()
57-
* lifecycle hook.
58-
*
59-
* This below useEffect() implementation runs only once, when
60-
* the app starts, after that, it never runs again.
44+
* In case we want the cleanup to be done on every render
45+
* cycle of the app, in that case, we don't send any 2nd
46+
* argument to the useEffect() lifecycle hook.
6147
*/
6248

6349
useEffect(() => {
64-
console.log("[Cockpit.jsx] iseEffect");
65-
// Dummy HttpRequest using setTimeout()
66-
setTimeout(() => {
67-
alert("Saved data to cloud!"); // executes after 1s
68-
}, 1000);
69-
}, []);
50+
console.log("[Cockpit.jsx] 2nd useEffect");
51+
return () => {
52+
console.log("[Cockpit.jsx] cleanup work in 2nd useEffect");
53+
};
54+
});
55+
56+
/**
57+
* This above useEffect() will be called every time the
58+
* Persons component is rendered on to the view as the 2nd
59+
* argument is missing.
60+
*/
7061

7162
const classes = [];
7263
let btnCockpitStyleClasses = "";

react-examples/src/components/Persons/Persons.component.jsx

Lines changed: 20 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,22 @@ class Persons extends Component {
6767
console.log(snapshot);
6868
}
6969

70+
/**
71+
* In an app where we have some kind of a live connection to
72+
* some backend, then we've a very realistic scenario where we
73+
* do have to cleanup some opened connection or some other
74+
* stuff. And for things like that, we use the lifecycle method
75+
* known as componentWillUnmount() for a class based component.
76+
*/
77+
78+
componentWillUnmount() {
79+
/**
80+
* In here, we can have any code that runs right before the
81+
* component is removed.
82+
*/
83+
console.log("[Persons.js] componentWillUnmount");
84+
}
85+
7086
render() {
7187
console.log("[Persons.jsx] rendering...");
7288
return this.props.persons.map((person, index) => {
@@ -90,46 +106,12 @@ export default Persons;
90106
* remove <React.strictMode> HOC that wraps the <App/>
91107
* component in index.js to run each lifecycle hook once.
92108
*
93-
* Output (before clicking the "Toggle Persons" button):
94-
* -----------------------------------------------------
95-
*
96-
* [App.js] constructor
97-
* [App.js] getDerivedStateFromProps {title: "Person Manager"}
98-
* [App.js] rendering...
99-
* [Cockpit.jsx] rendering...
100-
* componentDidMount
101-
*
102-
*
103-
* Output (after clicking "Toggle Persons" button)
104-
* -----------------------------------------------
105-
*
106-
* [App.js] getDerivedStateFromProps {title: "Person Manager"}
107-
* [App.js] rendering...
108-
* [Cockpit.jsx] rendering...
109-
* [Persons.jsx] rendering...
110-
* [Person.jsx] rendering...
111-
*
112-
*
113-
* Output (when changing a name of some person, and when the Math.random() > 0.5)
114-
* -----------------------------------------------------------------------------------
115-
*
116-
* [App.js] getDerivedStateFromProps {title: "Person Manager"}
117-
* [App.js] rendering...
118-
* [Cockpit.jsx] rendering...
119-
* [Persons.jsx] shouldComponentUpdate
120-
* [Persons.jsx] rendering...
121-
* [Person.jsx] rendering...
122-
* [Persons.jsx] getSnapshotBeforeUpdate
123-
* [Persons.jsx] componentDidUpdate
124-
* > {message: "Hello 🖖 🖖 from getSnapshotBeforeUpdate()"}
125-
*
126-
*
127-
* Output (when changing a name of some person, and when the Math.random() <= 0.5)
128-
* ------------------------------------------------------------------------------------
109+
* Output (after clicking the "Toggle Persons" button when the list of Persons are listed on the view)
110+
* ---------------------------------------------------------------------------------------------------
129111
*
130112
* [App.js] getDerivedStateFromProps {title: "Person Manager"}
113+
* [App.js] shouldComponentUpdate
131114
* [App.js] rendering...
132-
* [Cockpit.jsx] rendering...
133-
* [Persons.jsx] shouldComponentUpdate
115+
* [Persons.js] componentWillUnmount
134116
*
135117
*/

react-examples/src/containers/App.js

Lines changed: 31 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ class App extends Component {
100100
{ name: 'Max', age: 28, id: 'a3' }
101101
],
102102
otherState: "This has some value",
103-
showPersons: false
103+
showPersons: false,
104+
showCockpit: true
104105
}
105106

106107
/**
@@ -207,12 +208,24 @@ class App extends Component {
207208

208209
return (
209210
<div className={AppStyleClasses.App}>
210-
<Cockpit
211-
title={this.props.title}
212-
showPersons={this.state.showPersons}
213-
persons={this.state.persons}
214-
clicked={this.togglePersonsHandler}
215-
/>
211+
<button
212+
onClick={() => {
213+
this.setState({
214+
showCockpit: !this.state.showCockpit,
215+
});
216+
}}
217+
>
218+
{this.state.showCockpit ? "Remove Cockpit" : "Show Cockpit"}
219+
</button>
220+
{
221+
this.state.showCockpit ?
222+
<Cockpit
223+
title={this.props.title}
224+
showPersons={this.state.showPersons}
225+
persons={this.state.persons}
226+
clicked={this.togglePersonsHandler}
227+
/> : null
228+
}
216229
{persons}
217230
</div>
218231
);
@@ -223,35 +236,20 @@ export default App;
223236

224237

225238
/**
226-
* Output: (on starting the app)
227-
* -----------------------------
228-
*
229-
* [App.js] constructor
230-
* [App.js] getDerivedStateFromProps {title: "Person Manager"}
231-
* [App.js] rendering...
232-
* [Cockpit.jsx] rendering...
233-
* [App.js] componentDidMount
234-
*
235-
*
236-
* Output (when "Toggle Persons" button is clicked and Math.random() <= 0.5)
237-
* -------------------------------------------------------------------------------
239+
* Output (when we press "Remove Cockpit" button and Math.random() > 0.5 in shouldComponentUpdate() lifecycle method inside Persons component)
240+
* -------------------------------------------------------------------------------------------------------------------------------------------
238241
*
239242
* [App.js] getDerivedStateFromProps {title: "Person Manager"}
240243
* [App.js] shouldComponentUpdate
241244
* [App.js] rendering...
242-
* [Cockpit.jsx] rendering...
243-
*
244-
*
245-
* Output (when "Toggle Persons" button is clicked and Math.random() > 0.5)
246-
* -----------------------------------------------------------------------------
247-
*
248-
* [App.js] getDerivedStateFromProps {title: "Person Manager"}
249-
* [App.js] shouldComponentUpdate
250-
* [App.js] rendering...
251-
* [Cockpit.jsx] rendering...
252-
* [Persons.jsx] rendering...
253-
* [Person.jsx] rendering...
254-
* [Person.jsx] rendering...
255-
* [Person.jsx] rendering...
245+
* [Cockpit.js] cleanup work in useEffect
256246
*
257247
*/
248+
249+
250+
/**
251+
* We can clearly see that useEffect() lifecycle hook's actual
252+
* body is not executed inside Cockpit component, only the
253+
* returned anonymous function from useEffect is executed
254+
* defined in line 23 of the Cockpit component.
255+
*/

0 commit comments

Comments
 (0)