question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

componentWillUnmount sometimes called after unmounting?

See original GitHub issue

Hi

I am experiencing a case where componentWillUnmount is called after it is unmounted.

See this fiddle: https://jsfiddle.net/0tdkrhhh/1/

And here is the minimal code:

import preact from 'preact';


class A extends preact.Component {
    componentDidMount() {
		console.log('A.componentDidMount, '+document.querySelectorAll('.a').length)
    }

    componentWillUnmount() {
		console.log('A.componentWillUnmount, '+document.querySelectorAll('.a').length)
		console.log(document.body.innerHTML)
    }

	render() {
		return (
			<p class="a">A</p>
		)
	}
}


const B = () => {
	return (
		<div></div>
	)
}


const app = document.getElementById('preact-app')

preact.render(
	<div id="preact-app">
		<A/>
		<B/>
	</div>,
	document.body,
	app
)

preact.render(
	<div id="preact-app">
		<B/>
	</div>,
	document.body,
	app
)

The output is

A.componentDidMount, 1        <-- DOM element was found 
A.componentWillUnmount, 0     <-- DOM element was not found
<div></div>                             <-- DOM structure

but as I understand the function componentWillUnmount the output should be

A.componentDidMount, 1        <-- DOM element was found 
A.componentWillUnmount, 1     <-- DOM element was found
<div><p class="a">A</p></div>      <-- DOM structure

By the way, if component <B> is omitted, everything works as expected.

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:6 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
zeromakecommented, Sep 25, 2017

@developit I found it vdom/diff.js#L228 This has been uninstalled but componentWillUnmount triggered on:

so top-level render and setState() are all the same

Repair the code

I need open a pull request?fix

From 7704dfe2bf85620f75da744e0574a21868fdea84 Mon Sep 17 00:00:00 2001
From: zeromake <a390720046@gmail.com>
Date: Mon, 25 Sep 2017 11:09:09 +0800
Subject: [PATCH] fix(vdom/diff): unmout after WillUnmount triggered

---
 src/vdom/diff.js          |  9 +++++--
 test/browser/lifecycle.js | 64 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 71 insertions(+), 2 deletions(-)

diff --git a/src/vdom/diff.js b/src/vdom/diff.js
index 6cc9bb7..6d936fd 100644
--- a/src/vdom/diff.js
+++ b/src/vdom/diff.js
@@ -190,6 +190,7 @@ function innerDiffNode(dom, vchildren, context, mountAll, isHydrating) {
 	}
 
 	if (vlen!==0) {
+		let offset = 0;
 		for (let i=0; i<vlen; i++) {
 			vchild = vchildren[i];
 			child = null;
@@ -219,13 +220,17 @@ function innerDiffNode(dom, vchildren, context, mountAll, isHydrating) {
 			// morph the matched/found/created DOM child to match vchild (deep)
 			child = idiff(child, vchild, context, mountAll);
 
-			f = originalChildren[i];
+			f = originalChildren[i + offset];
 			if (child && child!==dom && child!==f) {
 				if (f==null) {
 					dom.appendChild(child);
 				}
 				else if (child===f.nextSibling) {
-					removeNode(f);
+					if (f._component) {
+						offset ++;
+					} else {
+						removeNode(f);
+					}
 				}
 				else {
 					dom.insertBefore(child, f);
diff --git a/test/browser/lifecycle.js b/test/browser/lifecycle.js
index 4a22c78..582df1b 100644
--- a/test/browser/lifecycle.js
+++ b/test/browser/lifecycle.js
@@ -586,4 +586,68 @@ describe('Lifecycle methods', () => {
 			}
 		});
 	});
+	describe("componentWillUnmount triggered", () => {
+		it("Component replace", (done) => {
+			class A extends Component {
+				componentWillUnmount() {
+					const len = document.querySelectorAll('.a').length;
+					expect(len).to.equal(1);
+					done();
+				}
+				render() {
+					return <p class="a">A</p>;
+				}
+			}
+			const B = () => <div class="b">B</div>;
+			class App extends Component {
+				constructor() {
+					super();
+					this.state = {
+						disable: true
+					};
+				}
+				render(props, state) {
+					return (
+						<div>{ state.disable ? <A/>: null }<B/></div>
+					);
+				}
+			}
+			let app;
+			render(<App ref={ c => app=c } />, scratch);
+			app.setState({
+				disable: false
+			});
+		});
+		it("Component'keys replace", (done) => {
+			class A extends Component {
+				componentWillUnmount() {
+					const len = document.querySelectorAll('.a').length;
+					expect(len).to.equal(1);
+					done();
+				}
+				render() {
+					return <p class="a">A</p>;
+				}
+			}
+			const B = () => <div class="b">B</div>;
+			class App extends Component {
+				constructor() {
+					super();
+					this.state = {
+						disable: true
+					};
+				}
+				render(props, state) {
+					return (
+						<div>{ state.disable ? <A key="a"/>: null }<B key="b"/></div>
+					);
+				}
+			}
+			let app;
+			render(<App ref={ c => app=c } />, scratch);
+			app.setState({
+				disable: false
+			});
+		});
+	});
 });
-- 
2.14.1


0reactions
marvinhagemeistercommented, Apr 14, 2019

This is fixed in master 👍

Read more comments on GitHub >

github_iconTop Results From Across the Web

Why did componentDidMount run after componentWillUnmount?
componentWillUnmount will be called once when the component is getting unmount. But I observed in the console that componentDidMount is being ...
Read more >
How to use componentWillUnmount with Functional ...
I want to display an dialog box on unmount and When user cancels it should stop unmounting the component. Right now, even if...
Read more >
ReactJS componentWillUnmount() Method - GeeksforGeeks
The componentWillUnmount() method allows us to execute the React code when the component gets destroyed or unmounted from the DOM (Document ...
Read more >
Component Lifecycle | Build with React JS
Components are unmounted when the parent component is no longer rendered or the parent component performs an update that does not render this ......
Read more >
Prevent React setState on unmounted Component
The shown warning(s) usually show up when this.setState() is called in a component even though the component got already unmounted.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found