diff --git a/packages/inula/scripts/__tests__/ComponentTest/StrictMode.test.js b/packages/inula/scripts/__tests__/ComponentTest/StrictMode.test.js new file mode 100644 index 00000000..a12fbed4 --- /dev/null +++ b/packages/inula/scripts/__tests__/ComponentTest/StrictMode.test.js @@ -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 ( + + + + + ); + }; + + const Child = () => { + const isMount = useRef(false); + + useEffect(() => { + if (!isMount.current) { + LogUtils.log('didMount'); + isMount.current = true; + } else { + LogUtils.log('didUpdate'); + } + }); + + return null; + }; + + act(() => render(, container)); + // 子组件初始化,会挂载一次 + expect(LogUtils.getAndClear()).toStrictEqual(['didMount']); + const button = container.querySelector('#btn'); + // 父组件State更新,子组件也会更新一次 + act(() => button.click()); + expect(LogUtils.getAndClear()).toStrictEqual(['didUpdate']); + }); +}); diff --git a/packages/inula/src/renderer/diff/nodeDiffComparator.ts b/packages/inula/src/renderer/diff/nodeDiffComparator.ts index bb07e3ef..e7161174 100644 --- a/packages/inula/src/renderer/diff/nodeDiffComparator.ts +++ b/packages/inula/src/renderer/diff/nodeDiffComparator.ts @@ -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标记为删除