fix: tidy-up name solving function and handle refetching and removing

This commit is contained in:
Josh 2023-01-10 13:58:55 +00:00
parent 168fe777d5
commit 8d6a247b14
6 changed files with 85 additions and 20 deletions

View File

@ -156,6 +156,13 @@
"type": "customField",
"regex": "^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$",
"customField": "plugin::color-picker.color"
},
"cats": {
"type": "dynamiczone",
"components": [
"basic.relation",
"basic.simple"
]
}
}
}

View File

@ -0,0 +1,14 @@
{
"collectionName": "components_basic_relations",
"info": {
"displayName": "Relation"
},
"options": {},
"attributes": {
"categories": {
"type": "relation",
"relation": "oneToMany",
"target": "api::category.category"
}
}
}

View File

@ -155,14 +155,28 @@ const reducer = (state, action) =>
const initialDataRelations = get(state, initialDataPath);
const modifiedDataRelations = get(state, modifiedDataPath);
set(draftState, initialDataPath, uniqBy([...value, ...initialDataRelations], 'id'));
/**
* Check if the values we're loading are already in initial
* data if they are then we don't need to load them at all
*/
const valuesToLoad = value.filter((relation) => {
return !initialDataRelations.some((initialDataRelation) => {
return initialDataRelation.id === relation.id;
});
});
set(draftState, initialDataPath, uniqBy([...valuesToLoad, ...initialDataRelations], 'id'));
/**
* We need to set the value also on modifiedData, because initialData
* and modifiedData need to stay in sync, so that the CM can compare
* both states, to render the dirty UI state
*/
set(draftState, modifiedDataPath, uniqBy([...value, ...modifiedDataRelations], 'id'));
set(
draftState,
modifiedDataPath,
uniqBy([...valuesToLoad, ...modifiedDataRelations], 'id')
);
break;
}

View File

@ -4,8 +4,6 @@ import React, { memo, useMemo, useState } from 'react';
import { useIntl } from 'react-intl';
import get from 'lodash/get';
import pick from 'lodash/pick';
import take from 'lodash/take';
import isNil from 'lodash/isNil';
import { useCMEditViewDataManager, NotAllowedInput } from '@strapi/helper-plugin';
@ -50,21 +48,22 @@ export const RelationInputDataManager = ({
relationReorder,
} = useCMEditViewDataManager();
const initialDataPath = [];
const nameSplit = name.split('.');
nameSplit.reduce((acc, currentValue, index) => {
const initialDataParent = get(initialData, initialDataPath);
const initialDataPath = nameSplit.reduce((acc, currentValue, index) => {
const initialDataParent = get(initialData, acc);
const modifiedDataTempKey = get(modifiedData, [
...take(nameSplit, index),
...nameSplit.slice(0, index),
currentValue,
'__temp_key__',
]);
if (!isNil(modifiedDataTempKey) && Array.isArray(initialDataParent)) {
if (Array.isArray(initialDataParent) && typeof modifiedDataTempKey === 'number') {
const initialDataIndex = initialDataParent.findIndex(
(entry) => entry.__temp_key__ === modifiedDataTempKey
);
acc.push(`${initialDataIndex}`);
acc.push(initialDataIndex.toString());
return acc;
}
@ -72,16 +71,14 @@ export const RelationInputDataManager = ({
acc.push(currentValue);
return acc;
}, initialDataPath);
}, []);
const relationsFromModifiedData = get(modifiedData, name, []);
const currentLastPage = Math.ceil(get(initialData, name, []).length / RELATIONS_TO_DISPLAY);
const cacheKey = `${slug}-${name}-${initialData?.id ?? ''}`;
const cacheKey = `${slug}-${initialDataPath.join('.')}`;
const { relations, search, searchFor } = useRelation(cacheKey, {
initialDataPath: ['initialData', ...initialDataPath],
modifiedDataPath: ['modifiedData', ...nameSplit],
relation: {
enabled: !!endpoints.relation,
endpoint: endpoints.relation,
@ -90,7 +87,15 @@ export const RelationInputDataManager = ({
...defaultParams,
pageSize: RELATIONS_TO_DISPLAY,
},
onLoad: relationLoad,
onLoad(value) {
relationLoad({
target: {
initialDataPath: ['initialData', ...initialDataPath],
modifiedDataPath: ['modifiedData', ...nameSplit],
value,
},
});
},
normalizeArguments: {
mainFieldName: mainField.name,
shouldAddLink: shouldDisplayRelationLink,

View File

@ -0,0 +1,23 @@
import * as React from 'react';
/**
* A custom hook that converts a callback to a ref to avoid triggering re-renders when passed as a
* prop or avoid re-executing effects when passed as a dependency
*
* @type {<T extends (...args: any[]) => any>(callback: T | undefined) => T}
*/
export const useCallbackRef = (callback) => {
const callbackRef = React.useRef(callback);
React.useEffect(() => {
callbackRef.current = callback;
});
// https://github.com/facebook/react/issues/19240
return React.useMemo(
() =>
(...args) =>
callbackRef.current?.(...args),
[]
);
};

View File

@ -5,7 +5,9 @@ import { axiosInstance } from '../../../core/utils';
import { normalizeRelations } from '../../components/RelationInputDataManager/utils';
export const useRelation = (cacheKey, { modifiedDataPath, initialDataPath, relation, search }) => {
import { useCallbackRef } from '../useCallbackRef';
export const useRelation = (cacheKey, { relation, search }) => {
const [searchParams, setSearchParams] = useState({});
const [currentPage, setCurrentPage] = useState(0);
@ -45,7 +47,7 @@ export const useRelation = (cacheKey, { modifiedDataPath, initialDataPath, relat
}
};
const { onLoad: onLoadRelationsCallback, normalizeArguments = {} } = relation;
const { onLoad: onLoadRelations, normalizeArguments = {} } = relation;
const relationsRes = useInfiniteQuery(['relation', cacheKey], fetchRelations, {
cacheTime: 0,
@ -121,15 +123,15 @@ export const useRelation = (cacheKey, { modifiedDataPath, initialDataPath, relat
}
}, [pageGoal, currentPage, fetchNextPage, hasNextPage, status]);
const onLoadRelationsCallback = useCallbackRef(onLoadRelations);
useEffect(() => {
if (status === 'success' && data && data.pages?.at(-1)?.results && onLoadRelationsCallback) {
// everytime we fetch, we normalize prior to adding to redux
const normalizedResults = normalizeRelations(data.pages.at(-1).results, normalizeArguments);
// this is loadRelation from EditViewDataManagerProvider
onLoadRelationsCallback({
target: { initialDataPath, modifiedDataPath, value: normalizedResults },
});
onLoadRelationsCallback(normalizedResults);
}
// eslint-disable-next-line react-hooks/exhaustive-deps