diff --git a/packages/runtime/__tests__/ImplWrapper/ImplWrapper.spec.tsx b/packages/runtime/__tests__/ImplWrapper/ImplWrapper.spec.tsx index bf38700f..2add3789 100644 --- a/packages/runtime/__tests__/ImplWrapper/ImplWrapper.spec.tsx +++ b/packages/runtime/__tests__/ImplWrapper/ImplWrapper.spec.tsx @@ -1,6 +1,7 @@ import '@testing-library/jest-dom/extend-expect'; import { render, screen, waitFor, act } from '@testing-library/react'; import produce from 'immer'; +import React from 'react'; import { initSunmaoUI } from '../../src'; import { TestLib } from '../testLib'; import { destroyTimesMap, renderTimesMap, clearTesterMap } from '../testLib/Tester'; @@ -11,6 +12,7 @@ import { MergeStateSchema, AsyncMergeStateSchema, TabsWithSlotsSchema, + ParentRerenderSchema, } from './mockSchema'; // A pure single sunmao component will render twice when it mount. @@ -68,6 +70,28 @@ describe('hidden trait condition', () => { }); }); +describe('when parent rerender change', () => { + it('the children should not rerender', () => { + const { App, stateManager, apiService } = initSunmaoUI({ libs: [TestLib] }); + stateManager.noConsoleError = true; + const { unmount } = render(); + const childTester = screen.getByTestId('tester'); + expect(childTester).toHaveTextContent(SingleComponentRenderTimes); + act(() => { + apiService.send('uiMethod', { + componentId: 'input', + name: 'setValue', + parameters: 'foo', + }); + }); + expect(childTester).toHaveTextContent(SingleComponentRenderTimes); + expect(stateManager.store['input'].value).toBe('foo'); + + unmount(); + clearTesterMap(); + }); +}); + describe('when component merge state synchronously', () => { it('it will not cause extra render', () => { const { App, stateManager } = initSunmaoUI({ libs: [TestLib] }); diff --git a/packages/runtime/__tests__/ImplWrapper/mockSchema.ts b/packages/runtime/__tests__/ImplWrapper/mockSchema.ts index 47ba3644..442484c6 100644 --- a/packages/runtime/__tests__/ImplWrapper/mockSchema.ts +++ b/packages/runtime/__tests__/ImplWrapper/mockSchema.ts @@ -84,6 +84,55 @@ export const HiddenTraitSchema: Application = { }, }; +export const ParentRerenderSchema: Application = { + version: 'sunmao/v1', + kind: 'Application', + metadata: { + name: 'some App', + }, + spec: { + components: [ + { + id: 'input', + type: 'test/v1/input', + properties: { + defaultValue: '', + }, + traits: [], + }, + { + id: 'stack6', + type: 'core/v1/stack', + properties: { + spacing: 12, + direction: 'horizontal', + align: 'auto', + wrap: '{{!!input.value}}', + justify: 'flex-start', + }, + traits: [], + }, + { + id: 'tester', + type: 'test/v1/tester', + properties: {}, + traits: [ + { + type: 'core/v1/slot', + properties: { + container: { + id: 'stack6', + slot: 'content', + }, + ifCondition: true, + }, + }, + ], + }, + ], + }, +}; + export const MergeStateSchema: Application = { version: 'sunmao/v1', kind: 'Application', diff --git a/packages/runtime/src/components/_internal/ImplWrapper/ImplWrapper.tsx b/packages/runtime/src/components/_internal/ImplWrapper/ImplWrapper.tsx index 7f2f7921..c667502a 100644 --- a/packages/runtime/src/components/_internal/ImplWrapper/ImplWrapper.tsx +++ b/packages/runtime/src/components/_internal/ImplWrapper/ImplWrapper.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { ImplWrapperProps } from '../../../types'; import { shallowCompare } from '@sunmao-ui/shared'; import { UnmountImplWrapper } from './UnmountImplWrapper'; +import { isEqual } from 'lodash'; export const ImplWrapper = React.memo( UnmountImplWrapper, @@ -10,20 +11,19 @@ export const ImplWrapper = React.memo( const nextChildren = nextProps.childrenMap[nextProps.component.id]?._grandChildren; const prevComponent = prevProps.component; const nextComponent = nextProps.component; - let isEqual = false; + let isComponentEqual = false; if (prevChildren && nextChildren) { - isEqual = shallowCompare(prevChildren, nextChildren); + isComponentEqual = shallowCompare(prevChildren, nextChildren); } else if (prevChildren === nextChildren) { - isEqual = true; + isComponentEqual = true; } - return ( - isEqual && + isComponentEqual && prevComponent === nextComponent && // TODO: keep ImplWrapper memorized and get slot props from store shallowCompare(prevProps.slotProps, nextProps.slotProps) && - shallowCompare(prevProps.slotContext, nextProps.slotContext) + isEqual(prevProps.slotContext, nextProps.slotContext) ); } );