From c47231909fd89578678b739bbe3c4c3961f401a4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=8D=A1=E9=A2=82?= <313439271@qq.com>
Date: Thu, 7 Jan 2021 10:49:38 +0800
Subject: [PATCH] =?UTF-8?q?fix:=20#11=20useEffect=20destroy=E5=87=BD?=
=?UTF-8?q?=E6=95=B0=E6=89=A7=E8=A1=8C=E6=97=B6=E6=9C=BA=E4=B8=8D=E5=AF=B9?=
=?UTF-8?q?=20(#13)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
packages/core/__tests__/hook-test.js | 261 +++++++++++++++++++++++++++
packages/fiber/commitWork.js | 42 +++--
packages/fiber/dispatcher.js | 15 +-
3 files changed, 291 insertions(+), 27 deletions(-)
create mode 100644 packages/core/__tests__/hook-test.js
diff --git a/packages/core/__tests__/hook-test.js b/packages/core/__tests__/hook-test.js
new file mode 100644
index 000000000..efee17e26
--- /dev/null
+++ b/packages/core/__tests__/hook-test.js
@@ -0,0 +1,261 @@
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @emails react-core
+ */
+
+'use strict';
+
+let React;
+let ReactDOM;
+let ReactTestUtils;
+
+describe('ReactElement', () => {
+ let ComponentClass;
+ let originalSymbol;
+
+ beforeEach(() => {
+ jest.resetModules();
+
+ // Delete the native Symbol if we have one to ensure we test the
+ // unpolyfilled environment.
+ originalSymbol = global.Symbol;
+ global.Symbol = undefined;
+
+ React = require('react');
+ ReactDOM = require('react-dom');
+ ReactTestUtils = require('test-utils');
+ // NOTE: We're explicitly not using JSX here. This is intended to test
+ // classic JS without JSX.
+ ComponentClass = class extends React.Component {
+ render() {
+ return React.createElement('div');
+ }
+ };
+ });
+
+ afterEach(() => {
+ global.Symbol = originalSymbol;
+ });
+
+
+ it('useEffect执行顺序', () => {
+ const container = document.createElement('div');
+ const {useEffect, useState} = React;
+
+ const testList = [];
+
+ function App() {
+ const [num, updateNum] = useState(0);
+ const [num2, updateNum2] = useState(0);
+ const [showChild, updateChildShow] = useState(true);
+
+ useEffect(() => {
+ testList.push(1);
+
+ return () => testList.push(999);
+ }, [])
+
+ // useLayoutEffect(() => {
+ // testList.push(100);
+
+ // return () => testList.push(99900);
+ // }, [])
+
+
+ useEffect(() => {
+ testList.push(8);
+ })
+
+ // useLayoutEffect(() => {
+ // testList.push(80);
+ // })
+
+ useEffect(() => {
+ testList.push(13);
+
+ return () => testList.push(14);
+ })
+
+ // useLayoutEffect(() => {
+ // testList.push(130);
+
+ // return () => testList.push(140);
+ // })
+
+ useEffect(() => {
+ testList.push(9);
+
+ return () => {
+ testList.push(10);
+ }
+ }, [num])
+
+ // useLayoutEffect(() => {
+ // testList.push(90);
+
+ // return () => {
+ // testList.push(100);
+ // }
+ // }, [num])
+
+ useEffect(() => {
+ testList.push(11);
+
+ return () => {
+ testList.push(12);
+ }
+ }, [num2])
+
+ // useLayoutEffect(() => {
+ // testList.push(110);
+
+ // return () => {
+ // testList.push(120);
+ // }
+ // }, [num2])
+
+
+ return (
+
+
+
+
+ {showChild && }
+
+ );
+ }
+
+ function Child({num, num2}) {
+ useEffect(() => {
+ testList.push(2);
+
+ return () => {
+ testList.push(3);
+ }
+ }, [num])
+
+ // useLayoutEffect(() => {
+ // testList.push(20);
+
+ // return () => {
+ // testList.push(30);
+ // }
+ // }, [num])
+
+ useEffect(() => {
+ testList.push(4);
+ return () => {
+ testList.push(5);
+ }
+ }, [])
+
+ // useLayoutEffect(() => {
+ // testList.push(40);
+ // return () => {
+ // testList.push(50);
+ // }
+ // }, [])
+
+ useEffect(() => {
+ testList.push(6);
+ return () => {
+ testList.push(6);
+ }
+ }, [num2])
+
+ // useLayoutEffect(() => {
+ // testList.push(60);
+ // return () => {
+ // testList.push(60);
+ // }
+ // }, [num2])
+
+ useEffect(() => {
+ testList.push(15);
+ return () => {
+ testList.push(16);
+ }
+ })
+
+ // useLayoutEffect(() => {
+ // testList.push(150);
+ // return () => {
+ // testList.push(160);
+ // }
+ // })
+
+ useEffect(() => {
+ testList.push(17);
+ return () => {
+ testList.push(18);
+ }
+ }, [true])
+
+ // useLayoutEffect(() => {
+ // testList.push(170);
+ // return () => {
+ // testList.push(180);
+ // }
+ // }, [true])
+
+ return num:{num} num2:{num2}
;
+ }
+
+ function GrandChild() {
+ useEffect(() => {
+ testList.push(21);
+
+ return () => {
+ testList.push(22);
+ }
+ })
+
+ // useLayoutEffect(() => {
+ // testList.push(210);
+
+ // return () => {
+ // testList.push(220);
+ // }
+ // })
+
+ useEffect(() => {
+ testList.push(23);
+
+ return () => {
+ testList.push(24);
+ }
+ }, [])
+
+ // useLayoutEffect(() => {
+ // testList.push(230);
+
+ // return () => {
+ // testList.push(240);
+ // }
+ // }, [])
+
+ return 'grand child';
+ }
+
+ const s = ReactDOM.render(, container);
+
+ ReactTestUtils.Simulate.click(s.refs.a);
+ ReactTestUtils.Simulate.click(s.refs.a);
+ ReactTestUtils.Simulate.click(s.refs.b);
+ ReactTestUtils.Simulate.click(s.refs.a);
+ ReactTestUtils.Simulate.click(s.refs.b);
+ ReactTestUtils.Simulate.click(s.refs.b);
+ ReactTestUtils.Simulate.click(s.refs.c);
+ ReactTestUtils.Simulate.click(s.refs.a);
+ ReactTestUtils.Simulate.click(s.refs.b);
+
+ const rightOrder = '21,23,2,4,6,15,17,1,8,13,9,11,22,21,3,16,2,15,14,10,8,13,9,22,21,3,16,2,15,14,10,8,13,9,22,21,6,16,6,15,14,12,8,13,11,22,21,3,16,2,15,14,10,8,13,9,22,21,6,16,6,15,14,12,8,13,11,22,21,6,16,6,15,14,12,8,13,11,3,5,6,16,18,22,24,14,8,13,14,10,8,13,9,14,12,8,13,11';
+
+ expect(testList.join()).toBe(rightOrder);
+ });
+
+});
\ No newline at end of file
diff --git a/packages/fiber/commitWork.js b/packages/fiber/commitWork.js
index e4f5142c8..a298854c0 100755
--- a/packages/fiber/commitWork.js
+++ b/packages/fiber/commitWork.js
@@ -212,25 +212,27 @@ export function disposeFibers(fiber) {
delete fiber.oldChildren;
fiber.children = {};
}
-function safeInvokeHooks(upateQueue, create, destory) {
- var uneffects = upateQueue[destory],
- effects = upateQueue[create], fn;
- if (!uneffects){
- return;
- }
- while ((fn = uneffects.shift())) {
- try {
- fn();
- } catch (e) { /** */ }
- }
- while ((fn = effects.shift())) {
- try {
- var f = fn();
- if (typeof f === 'function') {
- uneffects.push(f);
+function safeInvokeHooks(upateQueue, create, destory, isUnmount) {
+ const prevDestroyList = upateQueue[destory];
+ const curCreateList = upateQueue[create];
+
+ curCreateList && curCreateList.forEach((createFn, i) => {
+ const depsChange = typeof createFn === 'function';
+
+ if (depsChange || isUnmount) {
+ const prevDestroyFn = prevDestroyList[i];
+ if (typeof prevDestroyFn === 'function') {
+ prevDestroyFn();
}
- } catch (e) { /** */ }
- }
+ }
+ });
+
+ !isUnmount && curCreateList && curCreateList.forEach((createFn, i) => {
+ if (typeof createFn === 'function') {
+ const destroyFn = createFn();
+ prevDestroyList[i] = destroyFn;
+ }
+ })
}
function disposeFiber(fiber, force) {
let { stateNode, effectTag } = fiber;
@@ -248,8 +250,8 @@ function disposeFiber(fiber, force) {
Renderer.onDispose(fiber);
if (fiber.hasMounted) {
if (isStateless) {
- safeInvokeHooks(fiber.updateQueue, 'layout', 'unlayout');
- safeInvokeHooks(fiber.updateQueue, 'passive', 'unpassive');
+ safeInvokeHooks(fiber.updateQueue, 'layout', 'unlayout', true);
+ safeInvokeHooks(fiber.updateQueue, 'passive', 'unpassive', true);
}
stateNode.updater.enqueueSetState = returnFalse;
guardCallback(stateNode, 'componentWillUnmount', []);
diff --git a/packages/fiber/dispatcher.js b/packages/fiber/dispatcher.js
index c2f0abd1a..2afcd076c 100755
--- a/packages/fiber/dispatcher.js
+++ b/packages/fiber/dispatcher.js
@@ -70,15 +70,16 @@ export function useCallbackImpl(create, deps, isMemo, isEffect) {//ok
}
export function useEffectImpl(create, deps, EffectTag, createList, destroyList) {//ok
let fiber = getCurrentFiber();
+ const hookIndex = hookCursor;
let updateQueue = fiber.updateQueue;
- if (useCallbackImpl(create, deps, false, true)) {//防止重复添加
- if (fiber.effectTag % EffectTag) {
- fiber.effectTag *= EffectTag;
- }
- let list = updateQueue[createList] || (updateQueue[createList] = []);
- updateQueue[destroyList] || (updateQueue[destroyList] = []);
- list.push(create);
+ const depsChange = !!useCallbackImpl(create, deps, false, true);
+
+ if (depsChange && fiber.effectTag % EffectTag) {
+ fiber.effectTag *= EffectTag;
}
+ updateQueue[createList] || (updateQueue[createList] = []);
+ updateQueue[destroyList] || (updateQueue[destroyList] = []);
+ updateQueue[createList][hookIndex] = depsChange && create;
}
export function useRef(initValue) {//ok
let fiber = getCurrentFiber();