Skip to content

Commit

Permalink
Merge pull request #15223 from strapi/chore/cm-relations-docs-components
Browse files Browse the repository at this point in the history
  • Loading branch information
joshuaellis committed Jan 18, 2023
2 parents 2ec1651 + e8fa36a commit f5e87de
Showing 1 changed file with 84 additions and 2 deletions.
86 changes: 84 additions & 2 deletions docs/docs/core/content-manager/relations.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ filter out the relation from the array of relations. This is handled inside the

:::note
Connecting relations adds the item to the end of the list, whilst loading more relations prepends to
the beginning of the list. This is the expected behaviour.
the beginning of the list. This is the expected behaviour, to keep the order of the list in the UI in sync with the API response.
:::

The `RelationInput` component takes the field in `modifiedData` as its source of truth. You could therefore consider this to
Expand All @@ -126,7 +126,7 @@ data for the api.

### Cleaning data to be posted to the API

The API to update the enttiy expects relations to be categorised into two groups, a `connect` array and `disconnect` array.
The API to update the entity expects relations to be categorised into two groups, a `connect` array and `disconnect` array.
You could do this as the user interacts with the input but we found this to be confusing and then involved us managing three
different arrays which makes the code more complex. Instead, because the browser doesn't really care about whats new and removed
and we have a copy of the slice of data we're mutating from the server we can run a small diff algorithm to determine which
Expand All @@ -142,3 +142,85 @@ relations have been connected and which have been disconnected. Returning an obj
```

## Frontend component architecture

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
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).

### `RelationInput`

This component is the presentational counterpart to the `RelationInputDataManager` component. It renders an input field based on the data passed from the data manager.

Under the hood it is using `react-window` to render a list of relations in a virtualized view. Some fields need to render thousands of relations, which
would otherwise have a negative impact on the overall performance of the content-manager.

## useRelation() hook

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

### `Options`

`option`s is a mandatory configuration and should implement the following shape:

```ts
type Options = {
name: string; // name of the relation field
relation: RelationConfiguration;
search: SearchConfiguration;
}

type RelationConfiguration = {
endpoint: string; // URL from where existing relations should be fetched
enabled: boolean; // defines whether relations should be fetched once the hook is called
pageParams: object; // additional query params which will be appended to `endpoint`
onLoad: (results: RelationResult[]) => void; // callback that will be fired after relations have been fetched (paginated)
normalizeArguments = {
mainFieldName: string; // name of the target model main field, determining which field to display (fallback: id)
shouldAddLink: boolean; // if the user is allowed to read the target model, the returned relations should include a link to the target
targetModel: object; // target content-type model
};
pageGoal: number; // the current page-count of the already loaded relations used to keep the redux store and query cache in sync.
}

type SearchConfiguration = {
endpoint: string; // URL from where new relations should be fetched
pageParams: object; // additional query params which will be appended to `endpoint`
}
```
### Return values
`relations` and `search` both return a consistent relation format:
```ts
type RelationResults = RelationResult[];

type RelationResult = {
id: number;
href?: string; // based on `shouldAddLink` and the `targetModel`
publicationState: 'draft' | 'published';
mainField: string; // will fallback to "id" if not set
}
```
#### `relations`
`relations` refers to a [inifinite-query return type](https://tanstack.com/query/v4/docs/react/guides/infinite-queries) from react-query. It exposes paginated relational data
aswell as methods to check if there are more pages or fetch more paginated results. Relations for a given field are fetched as soon as the hook is called.
#### `search`
`search` refers to a [inifinite-query return type](https://tanstack.com/query/v4/docs/react/guides/infinite-queries) from react-query. It exposes paginated search results
for a relational field. Search results are only fetched after `searchFor()` has been called.
#### `searchFor(string)`
`searchFor` is a method which can be used to search for entities which haven't been connected with the source entity yet. The method accepts a search-term: `searchFor("term")`.

0 comments on commit f5e87de

Please sign in to comment.