fix(ImplWrapper): deep compare slotContext in memo

This commit is contained in:
Bowen Tan 2022-07-27 16:07:25 +08:00
parent 941ffa9089
commit 427c74b8c8
3 changed files with 79 additions and 6 deletions

View File

@ -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(<App options={ParentRerenderSchema} />);
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] });

View File

@ -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',

View File

@ -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<ImplWrapperProps>(
UnmountImplWrapper,
@ -10,20 +11,19 @@ export const ImplWrapper = React.memo<ImplWrapperProps>(
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)
);
}
);