Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tiny piece of documentation around the relation component structure #15223

Merged
merged 4 commits into from
Jan 18, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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`
gu-stav marked this conversation as resolved.
Show resolved Hide resolved

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