memomize slotElements to avoid extra re-mount

This commit is contained in:
Yanzhen Yu 2022-05-13 01:31:19 +08:00
parent 3781cecf1f
commit cc41138c8e
6 changed files with 48 additions and 20 deletions

View File

@ -14,7 +14,14 @@ export const ImplWrapper = React.memo<ImplWrapperProps>(
if (prevChildren && nextChildren) {
isEqual = shallowCompareArray(prevChildren, nextChildren);
} else if (prevChildren === nextChildren) {
isEqual = true;
}
return isEqual && prevComponent === nextComponent;
return (
isEqual &&
prevComponent === nextComponent &&
// TODO: keep ImplWrapper memorized and get slot props from store
prevProps.slotProps === nextProps.slotProps
);
}
);

View File

@ -4,13 +4,13 @@ import { RuntimeTraitSchema } from '@sunmao-ui/core';
import { watch } from '../../../utils/watchReactivity';
import { ImplWrapperProps, TraitResult } from '../../../types';
import { useRuntimeFunctions } from './hooks/useRuntimeFunctions';
import { useSlotElements } from './hooks/useSlotChildren';
import { getSlotElements } from './hooks/useSlotChildren';
import { useGlobalHandlerMap } from './hooks/useGlobalHandlerMap';
import { useEleRef } from './hooks/useEleMap';
import { useGridLayout } from './hooks/useGridLayout';
export const ImplWrapperMain = React.forwardRef<HTMLDivElement, ImplWrapperProps>(
(props, ref) => {
function ImplWrapperMain(props, ref) {
const { component: c, children } = props;
const { registry, stateManager } = props.services;
@ -138,7 +138,32 @@ export const ImplWrapperMain = React.forwardRef<HTMLDivElement, ImplWrapperProps
);
const unmount = traitResults.some(result => result.unmount);
const slotElements = useSlotElements(props);
const slotElements = useMemo(() => {
return getSlotElements({
app: props.app,
childrenMap: props.childrenMap,
children: props.children,
component: props.component,
gridCallbacks: props.gridCallbacks,
services: props.services,
hooks: props.hooks,
isInModule: props.isInModule,
});
}, [
/**
* exclude props.slotProps from dependency,
* otherwise, new slotProps will create new slotElements,
* which cause extra re-mount
*/
props.app,
props.children,
props.childrenMap,
props.component,
props.gridCallbacks,
props.hooks,
props.isInModule,
props.services,
]);
const C = unmount ? null : (
<Impl

View File

@ -7,7 +7,7 @@ import { ImplWrapperProps, TraitResult } from '../../../types';
import { watch } from '../../..';
export const UnmountImplWrapper = React.forwardRef<HTMLDivElement, ImplWrapperProps>(
(props, ref) => {
function UnmountImplWrapper(props, ref) {
const { component: c, services } = props;
const { stateManager, registry } = services;
const { executeTrait } = useRuntimeFunctions(props);

View File

@ -1,13 +1,12 @@
import React from 'react';
import { RuntimeComponentSchema, SlotSchema } from '@sunmao-ui/core';
import { SlotSchema } from '@sunmao-ui/core';
import { ImplWrapperProps, SlotsElements } from '../../../../types';
import { ImplWrapper } from '../ImplWrapper';
export function useSlotElements(
props: ImplWrapperProps
export function getSlotElements(
props: ImplWrapperProps & { children?: React.ReactNode }
): SlotsElements<Record<string, SlotSchema>> {
const { component: c, childrenMap } = props;
const childrenCache = new Map<RuntimeComponentSchema, React.ReactElement>();
if (!childrenMap[c.id]) {
return {};
@ -15,14 +14,10 @@ export function useSlotElements(
const slotElements: SlotsElements<Record<string, SlotSchema>> = {};
for (const slot in childrenMap[c.id]) {
const slotChildren = childrenMap[c.id][slot].map(child => {
if (!childrenCache.get(child)) {
const ele = <ImplWrapper key={child.id} {...props} component={child} />;
childrenCache.set(child, ele);
}
return childrenCache.get(child)!;
return <ImplWrapper key={child.id} {...props} component={child} />;
});
slotElements[slot] = slotProps => {
slotElements[slot] = function inlineSlot(slotProps) {
return <>{slotChildren.map(child => React.cloneElement(child, { slotProps }))}</>;
};
}

View File

@ -7,7 +7,8 @@ import relativeTime from 'dayjs/plugin/relativeTime';
import LocalizedFormat from 'dayjs/plugin/localizedFormat';
import { isProxy, reactive, toRaw } from '@vue/reactivity';
import { watch } from '../utils/watchReactivity';
import { isNumeric, parseExpression, consoleError, ConsoleType, type ExpChunk } from '@sunmao-ui/shared';
import { isNumeric, parseExpression, consoleError, ConsoleType } from '@sunmao-ui/shared';
import type { ExpChunk } from '@sunmao-ui/shared';
dayjs.extend(relativeTime);
dayjs.extend(isLeapYear);
@ -35,7 +36,7 @@ export class ExpressionError extends Error {
}
}
export type StateManagerInterface = InstanceType<typeof StateManager>
export type StateManagerInterface = InstanceType<typeof StateManager>;
export class StateManager {
store = reactive<Record<string, any>>({});

View File

@ -1,6 +1,6 @@
import { Type } from '@sinclair/typebox';
import { CORE_VERSION, CoreTraitName } from '@sunmao-ui/shared';
import { implementRuntimeTrait } from 'src/utils/buildKit';
import { implementRuntimeTrait } from '../../utils/buildKit';
const ContainerPropertySpec = Type.Object({
id: Type.String(),