mirror of
https://github.com/strapi/strapi.git
synced 2025-11-02 10:55:37 +00:00
chore: merge main
This commit is contained in:
commit
9799b7c67b
@ -9,6 +9,7 @@ import { Stack } from '@strapi/design-system/Stack';
|
||||
import { useContentTypeLayout } from '../../hooks';
|
||||
import FieldComponent from '../FieldComponent';
|
||||
import Inputs from '../Inputs';
|
||||
import useLazyComponents from '../../hooks/useLazyComponents';
|
||||
|
||||
const NonRepeatableComponent = ({ componentUid, isFromDynamicZone, isNested, name }) => {
|
||||
const { getComponentLayout } = useContentTypeLayout();
|
||||
@ -18,6 +19,8 @@ const NonRepeatableComponent = ({ componentUid, isFromDynamicZone, isNested, nam
|
||||
);
|
||||
const fields = componentLayoutData.layouts.edit;
|
||||
|
||||
const { lazyComponentStore } = useLazyComponents();
|
||||
|
||||
return (
|
||||
<Box
|
||||
background={isFromDynamicZone ? '' : 'neutral100'}
|
||||
@ -67,6 +70,7 @@ const NonRepeatableComponent = ({ componentUid, isFromDynamicZone, isNested, nam
|
||||
metadatas={metadatas}
|
||||
queryInfos={queryInfos}
|
||||
size={size}
|
||||
customFieldInputs={lazyComponentStore}
|
||||
/>
|
||||
</GridItem>
|
||||
);
|
||||
|
||||
@ -28,6 +28,7 @@ import Inputs from '../../Inputs';
|
||||
import FieldComponent from '../../FieldComponent';
|
||||
|
||||
import Preview from './Preview';
|
||||
import useLazyComponents from '../../../hooks/useLazyComponents';
|
||||
|
||||
const CustomIconButton = styled(IconButton)`
|
||||
background-color: transparent;
|
||||
@ -124,6 +125,8 @@ const DraggedItem = ({
|
||||
const composedAccordionRefs = composeRefs(accordionRef, dragRef);
|
||||
const composedBoxRefs = composeRefs(boxRef, dropRef);
|
||||
|
||||
const { lazyComponentStore } = useLazyComponents();
|
||||
|
||||
return (
|
||||
<Box ref={composedBoxRefs}>
|
||||
{isDragging ? (
|
||||
@ -212,6 +215,7 @@ const DraggedItem = ({
|
||||
metadatas={metadatas}
|
||||
queryInfos={queryInfos}
|
||||
size={size}
|
||||
customFieldInputs={lazyComponentStore}
|
||||
/>
|
||||
</GridItem>
|
||||
);
|
||||
|
||||
@ -1,44 +1,69 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useCustomFields } from '@strapi/helper-plugin';
|
||||
|
||||
const componentStore = new Map();
|
||||
|
||||
/**
|
||||
* @description
|
||||
* A hook to lazy load custom field components
|
||||
* @param {Array.<string>} componentUids - The uids to look up components
|
||||
* @returns object
|
||||
*/
|
||||
const useLazyComponents = (componentUids) => {
|
||||
const [lazyComponentStore, setLazyComponentStore] = useState({});
|
||||
const [loading, setLoading] = useState(true);
|
||||
const useLazyComponents = (componentUids = []) => {
|
||||
const [lazyComponentStore, setLazyComponentStore] = useState(Object.fromEntries(componentStore));
|
||||
const [loading, setLoading] = useState(() => {
|
||||
if (componentStore.size === 0 && componentUids.length > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
const customFieldsRegistry = useCustomFields();
|
||||
|
||||
useEffect(() => {
|
||||
const setStore = (store) => {
|
||||
setLazyComponentStore(store);
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
const lazyLoadComponents = async (uids, components) => {
|
||||
const modules = await Promise.all(components);
|
||||
|
||||
uids.forEach((uid, index) => {
|
||||
if (!Object.keys(lazyComponentStore).includes(uid)) {
|
||||
setLazyComponentStore({ ...lazyComponentStore, [uid]: modules[index].default });
|
||||
}
|
||||
componentStore.set(uid, modules[index].default);
|
||||
});
|
||||
|
||||
setStore(Object.fromEntries(componentStore));
|
||||
};
|
||||
|
||||
if (componentUids.length) {
|
||||
const componentPromises = componentUids.map((uid) => {
|
||||
if (componentUids.length && loading) {
|
||||
/**
|
||||
* These uids are not in the component store therefore we need to get the components
|
||||
*/
|
||||
const newUids = componentUids.filter((uid) => !componentStore.get(uid));
|
||||
|
||||
const componentPromises = newUids.map((uid) => {
|
||||
const customField = customFieldsRegistry.get(uid);
|
||||
|
||||
return customField.components.Input();
|
||||
});
|
||||
|
||||
lazyLoadComponents(componentUids, componentPromises);
|
||||
if (componentPromises.length > 0) {
|
||||
lazyLoadComponents(newUids, componentPromises);
|
||||
}
|
||||
}
|
||||
}, [componentUids, customFieldsRegistry, loading]);
|
||||
|
||||
if (componentUids.length === Object.keys(lazyComponentStore).length) {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [componentUids, customFieldsRegistry, loading, lazyComponentStore]);
|
||||
/**
|
||||
* Wrap this in a callback so it can be used in
|
||||
* effects to cleanup the cached store if required
|
||||
*/
|
||||
const cleanup = useCallback(() => {
|
||||
componentStore.clear();
|
||||
setLazyComponentStore({});
|
||||
}, []);
|
||||
|
||||
return { isLazyLoading: loading, lazyComponentStore };
|
||||
return { isLazyLoading: loading, lazyComponentStore, cleanup };
|
||||
};
|
||||
|
||||
export default useLazyComponents;
|
||||
|
||||
@ -26,25 +26,50 @@ jest.mock('@strapi/helper-plugin', () => ({
|
||||
}));
|
||||
|
||||
describe('useLazyComponents', () => {
|
||||
let cleanup;
|
||||
|
||||
afterEach(() => {
|
||||
if (typeof cleanup === 'function') {
|
||||
cleanup();
|
||||
cleanup = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
it('lazy loads the components', async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
const { result, waitFor } = renderHook(() => useLazyComponents(['plugin::test.test']));
|
||||
|
||||
cleanup = result.current.cleanup;
|
||||
|
||||
expect(result.current.isLazyLoading).toEqual(true);
|
||||
expect(result.current.lazyComponentStore).toEqual({});
|
||||
|
||||
await waitFor(() => expect(result.current.isLazyLoading).toEqual(false));
|
||||
|
||||
expect(result.current.lazyComponentStore['plugin::test.test']).toBeDefined();
|
||||
});
|
||||
|
||||
test('assuming the store has been initialised before hand, other hooks called should be able to access the global cache', async () => {
|
||||
const { result: initialResult, waitFor } = renderHook(() =>
|
||||
useLazyComponents(['plugin::test.test'])
|
||||
);
|
||||
|
||||
expect(result.current).toEqual({ isLazyLoading: true, lazyComponentStore: {} });
|
||||
|
||||
await waitForNextUpdate();
|
||||
|
||||
expect(JSON.stringify(result.current)).toEqual(
|
||||
JSON.stringify({
|
||||
isLazyLoading: false,
|
||||
lazyComponentStore: { 'plugin::test.test': jest.fn() },
|
||||
})
|
||||
await waitFor(() =>
|
||||
expect(initialResult.current.lazyComponentStore['plugin::test.test']).toBeDefined()
|
||||
);
|
||||
|
||||
const { result: actualResult, waitFor: secondWaitFor } = renderHook(() => useLazyComponents());
|
||||
|
||||
cleanup = actualResult.current.cleanup;
|
||||
|
||||
await secondWaitFor(() => expect(actualResult.current.isLazyLoading).toBe(false));
|
||||
|
||||
expect(actualResult.current.lazyComponentStore['plugin::test.test']).toBeDefined();
|
||||
});
|
||||
it('handles no components to load', async () => {
|
||||
|
||||
test('given there are no components to load it should not be loading and the store should be empty', async () => {
|
||||
const { result } = renderHook(() => useLazyComponents([]));
|
||||
|
||||
expect(result.current).toEqual({ isLazyLoading: false, lazyComponentStore: {} });
|
||||
expect(result.current.isLazyLoading).toEqual(false);
|
||||
expect(result.current.lazyComponentStore).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user