mirror of
https://github.com/strapi/strapi.git
synced 2025-12-24 13:43:41 +00:00
fix: tidy-up name solving function and handle refetching and removing
This commit is contained in:
parent
168fe777d5
commit
8d6a247b14
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
14
examples/getstarted/src/components/basic/relation.json
Normal file
14
examples/getstarted/src/components/basic/relation.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"collectionName": "components_basic_relations",
|
||||
"info": {
|
||||
"displayName": "Relation"
|
||||
},
|
||||
"options": {},
|
||||
"attributes": {
|
||||
"categories": {
|
||||
"type": "relation",
|
||||
"relation": "oneToMany",
|
||||
"target": "api::category.category"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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),
|
||||
[]
|
||||
);
|
||||
};
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user