Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 53 additions & 6 deletions packages/devextreme-react/src/core/component-base.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ type ComponentBaseProps = ComponentProps & {
interface ComponentBaseRef {
getInstance: () => any;
getElement: () => HTMLDivElement | undefined;
createWidget: (element?: Element) => void;
createWidget: (element?: Element) => boolean;
}

interface IHtmlOptions {
Expand Down Expand Up @@ -155,6 +155,14 @@ const ComponentBase = forwardRef<ComponentBaseRef, any>(
}
}, []);

const getInstanceHost = (inst: any): Element | undefined => {
try {
return inst?.element?.();
} catch {
return undefined;
}
};

const setInlineStyles = useCallback((styles) => {
if (element.current) {
const el = element.current;
Expand Down Expand Up @@ -217,12 +225,18 @@ const ComponentBase = forwardRef<ComponentBaseRef, any>(
unscheduleGuards();
}, []);

const createWidget = useCallback((el?: Element) => {
beforeCreateWidget();

const createWidget = useCallback((el?: Element): boolean => {
// eslint-disable-next-line no-param-reassign
el = el || element.current;

const currentHost = getInstanceHost(instance.current);
if (instance.current && el && currentHost === el) {
return false; // reuse existing instance on same host (Activity resume)
}

// Only run these hooks when we actually create
beforeCreateWidget();

let options: any = {
templatesRenderAsynchronously: true,
...optionsManager.current.getInitialOptions(widgetConfig),
Expand Down Expand Up @@ -255,6 +269,7 @@ const ComponentBase = forwardRef<ComponentBaseRef, any>(
instance.current.on('optionChanged', optionsManager.current.onOptionChanged);

afterCreateWidget();
return true;
}, [
beforeCreateWidget,
afterCreateWidget,
Expand Down Expand Up @@ -294,6 +309,18 @@ const ComponentBase = forwardRef<ComponentBaseRef, any>(
]);

const onComponentMounted = useCallback(() => {
// Activity resume: instance exists but effects were torn down -> refresh layout after showing
if (instance.current) {
requestAnimationFrame(() => {
const inst = instance.current;
if (inst?.updateDimensions) {
inst.updateDimensions();
} else if (inst?.repaint) {
inst.repaint();
}
});
}

const { style } = props;

if (childElementsDetached.current) {
Expand All @@ -309,12 +336,13 @@ const ComponentBase = forwardRef<ComponentBaseRef, any>(

prevPropsRef.current = props;
}, [
restoreTree,
updateCssClasses,
setInlineStyles,
props,
]);

const onComponentUnmounted = useCallback(() => {
const disposeWidgetNow = useCallback(() => {
removalLocker?.lock();

if (instance.current) {
Expand All @@ -340,6 +368,25 @@ const ComponentBase = forwardRef<ComponentBaseRef, any>(
removalLocker?.unlock();
}, [removalLocker]);

const onComponentUnmounted = useCallback(() => {
const el = element.current;
const disposedRef = { disposed: false };

const checkAndDispose = () => {
if (disposedRef.disposed) return;

// If element is still connected, it is likely Activity hide -> keep instance
if (el?.isConnected) return;

disposedRef.disposed = true;
disposeWidgetNow();
};

// Run in both microtask and macrotask to avoid timing edge cases
queueMicrotask(checkAndDispose);
setTimeout(checkAndDispose, 0);
}, [disposeWidgetNow]);

useLayoutEffect(() => {
onComponentMounted();

Expand Down Expand Up @@ -371,7 +418,7 @@ const ComponentBase = forwardRef<ComponentBaseRef, any>(
return element.current;
},
createWidget(el) {
createWidgetRef.current?.(el);
return createWidgetRef.current?.(el) ?? false;
},
}
), []);
Expand Down
30 changes: 23 additions & 7 deletions packages/devextreme-react/src/core/component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ const Component = forwardRef<ComponentRef, any>(
},
), [props, registerExtension]);

const createWidget = useCallback((el?: Element) => {
componentBaseRef.current?.createWidget(el);
}, []);
const createWidget = useCallback(
(el?: Element) => componentBaseRef.current?.createWidget(el) ?? false, []
);

const clearExtensions = useCallback(() => {
props.clearExtensions?.();
Expand All @@ -76,11 +76,27 @@ const Component = forwardRef<ComponentRef, any>(
const clearExtensionsRef = useRef(clearExtensions);

useLayoutEffect(() => {
createWidget();
createExtensions();
const created = createWidget();
if (created) {
createExtensions();
}

return () => {
clearExtensionsRef.current?.();
const el = componentBaseRef.current?.getElement();
const clearedRef = { cleared: false };

const checkAndClear = () => {
if (clearedRef.cleared) return;

// If still connected, it's likely Activity hide -> do nothing
if (el?.isConnected) return;

clearedRef.cleared = true;
clearExtensionsRef.current?.();
};

queueMicrotask(checkAndClear);
setTimeout(checkAndClear, 0);
};
}, []);

Expand All @@ -101,7 +117,7 @@ const Component = forwardRef<ComponentRef, any>(
return componentBaseRef.current?.getElement();
},
createWidget(el) {
createWidgetRef.current?.(el);
return createWidgetRef.current?.(el) ?? false;
},
clearExtensions() {
clearExtensionsRef.current?.();
Expand Down
8 changes: 4 additions & 4 deletions packages/devextreme-react/src/core/extension-component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ const ExtensionComponent = forwardRef<ComponentBaseRef, any>(
<P extends IHtmlOptions>(props: P & ComponentProps, ref: React.ForwardedRef<ComponentBaseRef>) => {
const componentBaseRef = useRef<ComponentBaseRef>(null);

const createWidget = useCallback((el?: Element) => {
componentBaseRef.current?.createWidget(el);
}, []);
const createWidget = useCallback(
(el?: Element) => componentBaseRef.current?.createWidget(el) ?? false, []
);

useLayoutEffect(() => {
const { onMounted } = props as any;
Expand All @@ -53,7 +53,7 @@ const ExtensionComponent = forwardRef<ComponentBaseRef, any>(
return componentBaseRef.current?.getElement();
},
createWidget(el) {
createWidgetRef.current?.(el);
return createWidgetRef.current?.(el) ?? false;
},
}
), []);
Expand Down
Loading