chore: merge main

This commit is contained in:
Josh 2022-12-05 11:30:46 +00:00
commit 9799b7c67b
4 changed files with 85 additions and 27 deletions

View File

@ -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>
);

View File

@ -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>
);

View File

@ -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;

View File

@ -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({});
});
});