修复StrictMode组件导致子组件状态丢失的问题

This commit is contained in:
huangxuan 2023-12-05 19:56:05 +08:00
parent 5b0ed0a5aa
commit 0022a6b55a
No known key found for this signature in database
GPG Key ID: E79F50C67022565D
2 changed files with 63 additions and 5 deletions

View File

@ -0,0 +1,58 @@
/*
* Copyright (c) 2023 Huawei Technologies Co.,Ltd.
*
* openInula is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
import * as Inula from '../../../src/index';
import { getLogUtils } from '../jest/testUtils';
describe('StrictMode Component test', () => {
const LogUtils = getLogUtils();
const { useState, useEffect, useRef, render, act } = Inula;
it('StrictMode is same to Fragment', () => {
const Parent = () => {
const [, setS] = useState('1');
return (
<Inula.StrictMode>
<button id="btn" onClick={() => setS(prevState => prevState + '!')}>
Click
</button>
<Child />
</Inula.StrictMode>
);
};
const Child = () => {
const isMount = useRef(false);
useEffect(() => {
if (!isMount.current) {
LogUtils.log('didMount');
isMount.current = true;
} else {
LogUtils.log('didUpdate');
}
});
return null;
};
act(() => render(<Parent />, container));
// 子组件初始化,会挂载一次
expect(LogUtils.getAndClear()).toStrictEqual(['didMount']);
const button = container.querySelector('#btn');
// 父组件State更新子组件也会更新一次
act(() => button.click());
expect(LogUtils.getAndClear()).toStrictEqual(['didUpdate']);
});
});

View File

@ -15,7 +15,7 @@
import type { VNode } from '../Types';
import { FlagUtils } from '../vnode/VNodeFlags';
import { TYPE_COMMON_ELEMENT, TYPE_FRAGMENT, TYPE_PORTAL } from '../../external/JSXElementType';
import { TYPE_COMMON_ELEMENT, TYPE_FRAGMENT, TYPE_PORTAL, TYPE_STRICT_MODE } from '../../external/JSXElementType';
import { DomText, DomPortal, Fragment, DomComponent } from '../vnode/VNodeTags';
import {
updateVNode,
@ -35,9 +35,9 @@ enum DiffCategory {
ARR_NODE = 'ARR_NODE',
}
// 检查是不是被 FRAGMENT 包裹
function isNoKeyFragment(child: any) {
return child != null && child.type === TYPE_FRAGMENT && child.key === null;
// 检查是不是被 FRAGMENT 或 StrictMode 包裹
function isNoKeyFragmentOrStrictMode(child: any) {
return child != null && (child.type === TYPE_FRAGMENT || child.type === TYPE_STRICT_MODE) && child.key === null;
}
// 清除单个节点
@ -631,7 +631,7 @@ export function createChildrenByDiff(
newChild: any,
isComparing: boolean
): VNode | null {
const isFragment = isNoKeyFragment(newChild);
const isFragment = isNoKeyFragmentOrStrictMode(newChild);
newChild = isFragment ? newChild.props.children : newChild;
// 1. 没有新节点直接把vNode标记为删除