mirror of
https://github.com/smartxworks/sunmao-ui.git
synced 2025-02-23 17:49:49 +08:00
fix(runtime): fix #590, use an init set to check first time render
This patch use a more solid way to check init state setup, instead of render count.
This commit is contained in:
parent
8e59b852ec
commit
c84d94a89a
@ -7,7 +7,7 @@ import { useRuntimeFunctions } from './hooks/useRuntimeFunctions';
|
||||
import { getSlotElements } from './hooks/useSlotChildren';
|
||||
import { useGlobalHandlerMap } from './hooks/useGlobalHandlerMap';
|
||||
import { useEleRef } from './hooks/useEleMap';
|
||||
import { initStateAndMethod } from '../../../utils/initStateAndMethod';
|
||||
import { initSingleComponentState } from '../../../utils/initStateAndMethod';
|
||||
import ComponentErrorBoundary from '../ComponentErrorBoundary';
|
||||
|
||||
export const ImplWrapperMain = React.forwardRef<HTMLDivElement, ImplWrapperProps>(
|
||||
@ -23,7 +23,7 @@ export const ImplWrapperMain = React.forwardRef<HTMLDivElement, ImplWrapperProps
|
||||
// This code is to init dynamic generated components
|
||||
// because they have not be initialized before
|
||||
if (!stateManager.store[c.id]) {
|
||||
initStateAndMethod(registry, stateManager, [c]);
|
||||
initSingleComponentState(registry, stateManager, c);
|
||||
}
|
||||
|
||||
const { eleRef, onRef, onRecoverFromError } = useEleRef(props);
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useEffect, useMemo, useState, useRef } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { RuntimeTraitSchema } from '@sunmao-ui/core';
|
||||
import { ImplWrapperMain } from './ImplWrapperMain';
|
||||
import { useRuntimeFunctions } from './hooks/useRuntimeFunctions';
|
||||
@ -12,8 +12,6 @@ export const UnmountImplWrapper = React.forwardRef<HTMLDivElement, ImplWrapperPr
|
||||
const { component: c, services, slotContext } = props;
|
||||
const { stateManager, registry } = services;
|
||||
const { executeTrait } = useRuntimeFunctions(props);
|
||||
const renderCount = useRef(0);
|
||||
renderCount.current++;
|
||||
|
||||
const slotKey = slotContext?.slotKey || '';
|
||||
|
||||
@ -39,9 +37,13 @@ export const UnmountImplWrapper = React.forwardRef<HTMLDivElement, ImplWrapperPr
|
||||
const traitChangeCallback = useCallback(
|
||||
(trait: RuntimeTraitSchema, properties: Record<string, unknown>) => {
|
||||
const result = executeTrait(trait, properties);
|
||||
|
||||
const prevIsHidden = isHidden;
|
||||
setIsHidden(!!result.unmount);
|
||||
if (result.unmount) {
|
||||
|
||||
const isLastRender = slotContext ? slotContext.renderSet.size === 0 : true;
|
||||
|
||||
if (result.unmount && isLastRender) {
|
||||
// Every component's state is initialized in initStateAnd Method.
|
||||
// So if if it should not render, we should remove it from store.
|
||||
|
||||
@ -49,19 +51,17 @@ export const UnmountImplWrapper = React.forwardRef<HTMLDivElement, ImplWrapperPr
|
||||
* prevIsHidden: Only clear the store when the component is not
|
||||
* hidden before this check.
|
||||
*
|
||||
* renderCount: Currently we call initStateAndMethod to setup the
|
||||
* state store, and let here to teardown the hidden components'
|
||||
* state. If a component is hidden forever, it still need to teardown
|
||||
* at the first time it rendered.
|
||||
* Not a perfect solution, and we should have better lifecycle
|
||||
* management for the state store.
|
||||
* initSet: when init set has the component's id, it means there
|
||||
* is an initial state setup by us. If a component is hidden
|
||||
* forever, it still need to teardown at the first time it rendered.
|
||||
*/
|
||||
if (!prevIsHidden || renderCount.current === 1) {
|
||||
if (!prevIsHidden || stateManager.initSet.has(c.id)) {
|
||||
delete stateManager.store[c.id];
|
||||
stateManager.initSet.delete(c.id);
|
||||
}
|
||||
}
|
||||
},
|
||||
[c.id, executeTrait, stateManager, isHidden]
|
||||
[c.id, executeTrait, stateManager, isHidden, slotContext]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -45,6 +45,18 @@ export class StateManager {
|
||||
store = reactive<Record<string, any>>({});
|
||||
slotStore = reactive<Record<string, any>>({});
|
||||
|
||||
/**
|
||||
* In `initStateAndMethod`, we setup all components' state with a
|
||||
* default value.
|
||||
*
|
||||
* If some components were unmounted later, the `UnmountImplWrapper`
|
||||
* will teardown the state.
|
||||
*
|
||||
* So we provide this flag set for the `UnmountImplWrapper`, let
|
||||
* it know whether the component's state is setup by the init process.
|
||||
*/
|
||||
initSet = new Set<string>();
|
||||
|
||||
dependencies: Record<string, unknown>;
|
||||
|
||||
mute = true;
|
||||
|
@ -15,7 +15,10 @@ export function initStateAndMethod(
|
||||
stateManager: StateManagerInterface,
|
||||
components: RuntimeComponentSchema[]
|
||||
) {
|
||||
components.forEach(c => initSingleComponentState(registry, stateManager, c));
|
||||
components.forEach(c => {
|
||||
stateManager.initSet.add(c.id);
|
||||
initSingleComponentState(registry, stateManager, c);
|
||||
});
|
||||
}
|
||||
|
||||
export function initSingleComponentState(
|
||||
@ -26,6 +29,7 @@ export function initSingleComponentState(
|
||||
if (stateManager.store[c.id]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let state = {};
|
||||
c.traits.forEach(t => {
|
||||
const tSpec = registry.getTrait(t.parsedType.version, t.parsedType.name).spec;
|
||||
|
Loading…
Reference in New Issue
Block a user