mirror of
https://github.com/strapi/strapi.git
synced 2025-11-03 19:36:20 +00:00
Merge pull request #15900 from strapi/fix/relations-caching-between-locales
This commit is contained in:
commit
3234fcc61d
@ -147,7 +147,7 @@ The input field for relation fields consist of two components:
|
||||
|
||||
### `RelationInputDataManager`
|
||||
|
||||
This container component handles data fetching and data normalization for the `RelationInput` component. This has been extracted from
|
||||
This container component handles data fetching and data normalization for the `RelationInput` component. This has been extracted from
|
||||
the `RelationInput` so that Strapi is able to move the underlying component into the design-system if the community would need it
|
||||
(most other input components can be consumed from there).
|
||||
|
||||
@ -163,7 +163,7 @@ would otherwise have a negative impact on the overall performance of the content
|
||||
This hook takes care of data-fetching and normalizes results relations aswell as search-results.
|
||||
|
||||
```ts
|
||||
const { relations: RelationResults, search: RelationResults, searchFor } = useRelation(reactQueryCacheKey: string, options: Options);
|
||||
const { relations: RelationResults, search: RelationResults, searchFor } = useRelation(reactQueryCacheKey: Array<string | object>, options: Options);
|
||||
```
|
||||
|
||||
### `Options`
|
||||
@ -205,10 +205,10 @@ type RelationResults = RelationResult[];
|
||||
|
||||
type RelationResult = {
|
||||
id: number;
|
||||
href?: string; // based on `shouldAddLink` and the `targetModel`
|
||||
href?: string; // based on `shouldAddLink` and the `targetModel`
|
||||
publicationState: 'draft' | 'published';
|
||||
mainField: string; // will fallback to "id" if not set
|
||||
}
|
||||
mainField: string; // will fallback to "id" if not set
|
||||
};
|
||||
```
|
||||
|
||||
#### `relations`
|
||||
|
||||
@ -53,6 +53,12 @@
|
||||
}
|
||||
},
|
||||
"type": "date"
|
||||
},
|
||||
"relation_locales": {
|
||||
"type": "relation",
|
||||
"relation": "manyToMany",
|
||||
"target": "api::relation-locale.relation-locale",
|
||||
"mappedBy": "categories"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,56 @@
|
||||
{
|
||||
"kind": "collectionType",
|
||||
"collectionName": "relation_locales",
|
||||
"info": {
|
||||
"singularName": "relation-locale",
|
||||
"pluralName": "relation-locales",
|
||||
"displayName": "Relations",
|
||||
"description": ""
|
||||
},
|
||||
"options": {
|
||||
"draftAndPublish": true
|
||||
},
|
||||
"pluginOptions": {
|
||||
"i18n": {
|
||||
"localized": true
|
||||
}
|
||||
},
|
||||
"attributes": {
|
||||
"categories": {
|
||||
"type": "relation",
|
||||
"relation": "manyToMany",
|
||||
"target": "api::category.category",
|
||||
"inversedBy": "relation_locales"
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"pluginOptions": {
|
||||
"i18n": {
|
||||
"localized": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"repeatable_relations": {
|
||||
"type": "component",
|
||||
"repeatable": true,
|
||||
"pluginOptions": {
|
||||
"i18n": {
|
||||
"localized": true
|
||||
}
|
||||
},
|
||||
"component": "basic.relation"
|
||||
},
|
||||
"dynamic_relations": {
|
||||
"pluginOptions": {
|
||||
"i18n": {
|
||||
"localized": true
|
||||
}
|
||||
},
|
||||
"type": "dynamiczone",
|
||||
"components": [
|
||||
"basic.relation",
|
||||
"basic.simple"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* relation-locale controller
|
||||
*/
|
||||
|
||||
const { createCoreController } = require('@strapi/strapi').factories;
|
||||
|
||||
module.exports = createCoreController('api::relation-locale.relation-locale');
|
||||
@ -0,0 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* relation-locale router
|
||||
*/
|
||||
|
||||
const { createCoreRouter } = require('@strapi/strapi').factories;
|
||||
|
||||
module.exports = createCoreRouter('api::relation-locale.relation-locale');
|
||||
@ -0,0 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* relation-locale service
|
||||
*/
|
||||
|
||||
const { createCoreService } = require('@strapi/strapi').factories;
|
||||
|
||||
module.exports = createCoreService('api::relation-locale.relation-locale');
|
||||
@ -156,11 +156,13 @@ const reducer = (state, action) =>
|
||||
const initialDataRelations = get(state, initialDataPath);
|
||||
const modifiedDataRelations = get(state, modifiedDataPath);
|
||||
|
||||
const valuesToLoad = value.filter((relation) => {
|
||||
return !initialDataRelations.some((initialDataRelation) => {
|
||||
return initialDataRelation.id === relation.id;
|
||||
});
|
||||
});
|
||||
const valuesToLoad = !initialDataRelations
|
||||
? value
|
||||
: value.filter((relation) => {
|
||||
return !initialDataRelations.some((initialDataRelation) => {
|
||||
return initialDataRelation.id === relation.id;
|
||||
});
|
||||
});
|
||||
|
||||
const keys = generateNKeysBetween(
|
||||
null,
|
||||
@ -282,9 +284,7 @@ const reducer = (state, action) =>
|
||||
* relationalFieldPaths won't be an array which is what we're expecting
|
||||
* Therefore we reset these bits of state to the correct data type
|
||||
* which is an array. Hence why we replace those fields.
|
||||
*
|
||||
*/
|
||||
|
||||
const mergeDataWithPreparedRelations = relationalFieldPaths
|
||||
.map((path) => path.split('.'))
|
||||
.reduce((acc, currentPaths) => {
|
||||
@ -301,7 +301,14 @@ const reducer = (state, action) =>
|
||||
return result;
|
||||
}, existingComponents);
|
||||
|
||||
if (state.modifiedData && get(state.modifiedData, componentName)) {
|
||||
if (
|
||||
state.modifiedData &&
|
||||
get(state.modifiedData, componentName) &&
|
||||
/**
|
||||
* Only replace the state if the entity ids are the same.
|
||||
*/
|
||||
state.modifiedData?.id === initialValues?.id
|
||||
) {
|
||||
/**
|
||||
* this will be null on initial load, however subsequent calls
|
||||
* will have data in them correlating to the names of the relational fields.
|
||||
|
||||
@ -107,8 +107,7 @@ const RelationInput = ({
|
||||
[totalNumberOfRelations, numberOfRelationsToDisplay]
|
||||
);
|
||||
|
||||
const shouldDisplayLoadMoreButton =
|
||||
(!!labelLoadMore && paginatedRelations.hasNextPage) || paginatedRelations.isLoading;
|
||||
const shouldDisplayLoadMoreButton = !!labelLoadMore && paginatedRelations.hasNextPage;
|
||||
|
||||
const options = useMemo(
|
||||
() =>
|
||||
|
||||
@ -295,23 +295,6 @@ describe('Content-Manager || RelationInput', () => {
|
||||
expect(screen.queryByText('Load more')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should display load more button loading if there is no next page but loading is true', () => {
|
||||
setup({
|
||||
relations: {
|
||||
data: [],
|
||||
isLoading: true,
|
||||
isSuccess: true,
|
||||
hasNextPage: false,
|
||||
isFetchingNextPage: false,
|
||||
},
|
||||
});
|
||||
|
||||
expect(screen.getByRole('button', { name: 'Load more' })).toHaveAttribute(
|
||||
'aria-disabled',
|
||||
'true'
|
||||
);
|
||||
});
|
||||
|
||||
test('should display error state', () => {
|
||||
setup({ error: 'This is an error' });
|
||||
|
||||
|
||||
@ -57,41 +57,47 @@ export const RelationInputDataManager = ({
|
||||
|
||||
const currentLastPage = Math.ceil(get(initialData, name, []).length / RELATIONS_TO_DISPLAY);
|
||||
|
||||
const cacheKey = `${slug}-${initialDataPath.join('.')}`;
|
||||
const { relations, search, searchFor } = useRelation(cacheKey, {
|
||||
relation: {
|
||||
enabled: !!endpoints.relation,
|
||||
endpoint: endpoints.relation,
|
||||
pageGoal: currentLastPage,
|
||||
pageParams: {
|
||||
...defaultParams,
|
||||
pageSize: RELATIONS_TO_DISPLAY,
|
||||
const { relations, search, searchFor } = useRelation(
|
||||
[slug, initialDataPath.join('.'), modifiedData.id, defaultParams],
|
||||
{
|
||||
relation: {
|
||||
enabled: !!endpoints.relation,
|
||||
endpoint: endpoints.relation,
|
||||
pageGoal: currentLastPage,
|
||||
pageParams: {
|
||||
...defaultParams,
|
||||
pageSize: RELATIONS_TO_DISPLAY,
|
||||
},
|
||||
onLoad(value) {
|
||||
relationLoad({
|
||||
target: {
|
||||
initialDataPath: ['initialData', ...initialDataPath],
|
||||
modifiedDataPath: ['modifiedData', ...nameSplit],
|
||||
value,
|
||||
},
|
||||
});
|
||||
},
|
||||
normalizeArguments: {
|
||||
mainFieldName: mainField.name,
|
||||
shouldAddLink: shouldDisplayRelationLink,
|
||||
targetModel,
|
||||
},
|
||||
},
|
||||
onLoad(value) {
|
||||
relationLoad({
|
||||
target: {
|
||||
initialDataPath: ['initialData', ...initialDataPath],
|
||||
modifiedDataPath: ['modifiedData', ...nameSplit],
|
||||
value,
|
||||
},
|
||||
});
|
||||
search: {
|
||||
endpoint: endpoints.search,
|
||||
pageParams: {
|
||||
...defaultParams,
|
||||
// eslint-disable-next-line no-nested-ternary
|
||||
entityId: isCreatingEntry
|
||||
? undefined
|
||||
: isComponentRelation
|
||||
? componentId
|
||||
: initialData.id,
|
||||
pageSize: SEARCH_RESULTS_TO_DISPLAY,
|
||||
},
|
||||
},
|
||||
normalizeArguments: {
|
||||
mainFieldName: mainField.name,
|
||||
shouldAddLink: shouldDisplayRelationLink,
|
||||
targetModel,
|
||||
},
|
||||
},
|
||||
search: {
|
||||
endpoint: endpoints.search,
|
||||
pageParams: {
|
||||
...defaultParams,
|
||||
// eslint-disable-next-line no-nested-ternary
|
||||
entityId: isCreatingEntry ? undefined : isComponentRelation ? componentId : initialData.id,
|
||||
pageSize: SEARCH_RESULTS_TO_DISPLAY,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
const isMorph = useMemo(() => relationType.toLowerCase().includes('morph'), [relationType]);
|
||||
const toOneRelation = [
|
||||
|
||||
@ -189,7 +189,7 @@ describe('RelationInputDataManager', () => {
|
||||
});
|
||||
|
||||
expect(useRelation).toBeCalledWith(
|
||||
expect.any(String),
|
||||
expect.arrayContaining([expect.any(String)]),
|
||||
expect.objectContaining({
|
||||
search: expect.objectContaining({
|
||||
pageParams: expect.objectContaining({
|
||||
|
||||
@ -33,7 +33,7 @@ const ComponentFixture = ({ children }) => (
|
||||
<QueryClientProvider client={client}>{children}</QueryClientProvider>
|
||||
);
|
||||
|
||||
const cacheKey = 'useRelation-cache-key';
|
||||
const cacheKey = ['useRelation-cache-key'];
|
||||
function setup(args) {
|
||||
return new Promise((resolve) => {
|
||||
act(() => {
|
||||
|
||||
@ -49,7 +49,7 @@ export const useRelation = (cacheKey, { relation, search }) => {
|
||||
|
||||
const { onLoad: onLoadRelations, normalizeArguments = {} } = relation;
|
||||
|
||||
const relationsRes = useInfiniteQuery(['relation', cacheKey], fetchRelations, {
|
||||
const relationsRes = useInfiniteQuery(['relation', ...cacheKey], fetchRelations, {
|
||||
cacheTime: 0,
|
||||
enabled: relation.enabled,
|
||||
/**
|
||||
@ -138,7 +138,7 @@ export const useRelation = (cacheKey, { relation, search }) => {
|
||||
}, [status, onLoadRelationsCallback, data]);
|
||||
|
||||
const searchRes = useInfiniteQuery(
|
||||
['relation', cacheKey, 'search', JSON.stringify(searchParams)],
|
||||
['relation', ...cacheKey, 'search', JSON.stringify(searchParams)],
|
||||
fetchSearch,
|
||||
{
|
||||
enabled: Object.keys(searchParams).length > 0,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user