Skip to content

Commit

Permalink
update docs and demo slightly (#275)
Browse files Browse the repository at this point in the history
- document `useImperativeReference` and mutations
- modify the pet demo to show the result of a mutation
  • Loading branch information
rbalicki2 authored Nov 14, 2024
1 parent 91d3020 commit dc7beae
Show file tree
Hide file tree
Showing 9 changed files with 174 additions and 26 deletions.
31 changes: 21 additions & 10 deletions demos/pet-demo/src/components/PetTaglineCard.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import React from 'react';
import React, { Suspense } from 'react';
import { iso } from '@iso';
import { Button, Card, CardContent } from '@mui/material';
import { useImperativeReference } from '@isograph/react';
import { FragmentReader, useImperativeReference } from '@isograph/react';
import { UNASSIGNED_STATE } from '@isograph/react-disposable-state';

export const PetTaglineCard = iso(`
field Pet.PetTaglineCard @component {
id
tagline
}
field Pet.PetTaglineCard @component {
id
tagline
}
`)(function PetTaglineCardComponent({ data: pet }) {
const {
fragmentReference: mutationRef,
Expand All @@ -23,7 +23,7 @@ field Pet.PetTaglineCard @component {
<CardContent>
<h2>Tagline</h2>
<p>&quot;{pet.tagline}&quot;</p>
{mutationRef == UNASSIGNED_STATE ? (
{mutationRef === UNASSIGNED_STATE ? (
<Button
onClick={() => {
loadMutation({
Expand All @@ -37,18 +37,29 @@ field Pet.PetTaglineCard @component {
>
Set tagline to SUPER DOG
</Button>
) : null}
) : (
<Suspense fallback="Mutation in flight">
<FragmentReader fragmentReference={mutationRef} />
</Suspense>
)}
</CardContent>
</Card>
);
});

export const setTagline = iso(`
field Mutation.SetTagline($input: SetPetTaglineParams!) {
field Mutation.SetTagline($input: SetPetTaglineParams!) @component {
set_pet_tagline(input: $input) {
pet {
tagline
}
}
}
`)(() => {});
`)(({ data }) => {
return (
<>
Nice! You updated the pet&apos;s tagline to{' '}
{data.set_pet_tagline?.pet?.tagline}!
</>
);
});
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { ExtractSecondParam, CombineWithIntrinsicAttributes } from '@isograph/react';
import type React from 'react';
import { setTagline as resolver } from '../../../PetTaglineCard';
export type Mutation__SetTagline__output_type = ReturnType<typeof resolver>;
export type Mutation__SetTagline__output_type = (React.FC<CombineWithIntrinsicAttributes<ExtractSecondParam<typeof resolver>>>);
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { EagerReaderArtifact, ReaderAst } from '@isograph/react';
import type {ComponentReaderArtifact, ExtractSecondParam, ReaderAst } from '@isograph/react';
import { Mutation__SetTagline__param } from './param_type';
import { Mutation__SetTagline__output_type } from './output_type';
import { setTagline as resolver } from '../../../PetTaglineCard';

const readerAst: ReaderAst<Mutation__SetTagline__param> = [
Expand Down Expand Up @@ -35,11 +34,12 @@ const readerAst: ReaderAst<Mutation__SetTagline__param> = [
},
];

const artifact: EagerReaderArtifact<
const artifact: ComponentReaderArtifact<
Mutation__SetTagline__param,
Mutation__SetTagline__output_type
ExtractSecondParam<typeof resolver>
> = {
kind: "EagerReaderArtifact",
kind: "ComponentReaderArtifact",
componentName: "Mutation.SetTagline",
resolver,
readerAst,
};
Expand Down
2 changes: 1 addition & 1 deletion demos/pet-demo/src/components/__isograph/iso.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ export function iso<T>(

export function iso<T>(
param: T & MatchesWhitespaceAndString<'field Mutation.SetTagline', T>
): IdentityWithParam<Mutation__SetTagline__param>;
): IdentityWithParamComponent<Mutation__SetTagline__param>;

export function iso<T>(
param: T & MatchesWhitespaceAndString<'field NewsfeedItem.NewsfeedAdOrBlog', T>
Expand Down
135 changes: 135 additions & 0 deletions docs-website/docs/mutation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# Mutations

In Isograph, mutations aren't special. The distinguishing feature of mutations is often that you want to make the network request at a specific time, for example, in response to a user clicking a button. This document describes how to make network requests in response to events, which you can use to trigger a mutation. Okay, onward!

## Walkthrough

### Defining the mutation field

First, define a client field on the `Mutation` object that calls the mutation you care about:

```js
export const setTagline = iso(`
field Mutation.SetTagline($input: SetPetTaglineParams!) {
set_pet_tagline(input: $input) {
pet {
tagline
}
}
}
`)((({data})) => data);
```

Make sure you select the fields that you want refetched and written into the store. In this case, we want to see the updated pet's tagline. We also return the data.

:::note
Note also that we're naming the field `Mutation.SetTagline`, _not_ `Mutation.set_pet_tagline`. There already is a field named `Mutation.set_pet_tagline`, defined in the schema. So, if you attempt to define a client field named `Mutation.set_pet_tagline`, the Isograph compiler will emit an error and refuse to compile!
:::

### Calling the mutation

Next, call `useImperativeReference`. This gives you back a fragment reference and a `loadFragmentReference` function:

```jsx
import { useImperativeReference } from '@isograph/react';
import { UNASSIGNED_STATE } from '@isograph/react-disposable-state';

export const PetTaglineCard = iso(`
field Pet.PetTaglineCard @component {
id
tagline
}
`)(function PetTaglineCardComponent({ data: pet }) {
const {
fragmentReference: mutationRef,
loadFragmentReference: loadMutation,
} = useImperativeReference(iso(`entrypoint Mutation.SetTagline`));
// ...
}
```
Next, call `loadFragmentReference` (`loadMutation` in this example) when a user clicks!
```jsx
{
mutationRef === UNASSIGNED_STATE ? (
<Button
onClick={() => {
loadMutation({
input: {
id: pet.id,
tagline: 'SUPER DOG',
},
});
}}
variant="contained"
>
Set tagline to SUPER DOG
</Button>
) : null;
}
```
Since we only want to set the tagline once, we check that `mutation === UNASSIGNED_STATE` before showing the button.
### Reading the results
What about reading the results of the mutation? There are two good ways to do this, and two additional ways that will work in the future.
First, we can wait until the network response completes and see the component re-render with the updated tagline.
For the second method, we can modify the mutation field as follows:
```js
export const setTagline = iso(`
field Mutation.SetTagline($input: SetPetTaglineParams!) @component {
set_pet_tagline(input: $input) {
pet {
tagline
}
}
}
`)((({data})) => {
return (
<>
Nice! You updated the pet's tagline to{' '}
{data.set_pet_tagline?.pet?.tagline}!
</>
);
});
```
Here, we add the `@component` annotation and return some JSX.
Now, we can use this! Modify the `PetTaglineCardComponent` component as follows:
```js
{
mutationRef === UNASSIGNED_STATE ? (
<Button
onClick={() => {
loadMutation({
input: {
id: pet.id,
tagline: 'SUPER DOG',
},
});
}}
variant="contained"
>
Set tagline to SUPER DOG
</Button>
) : (
<Suspense fallback="Mutation in flight">
<FragmentReader fragmentReference={mutationRef} />
</Suspense>
);
}
```
#### Additional methods
The following methods will be available in the future:
- When reading a fragment reference is not a hook (see [this issue](https://github.com/isographlabs/isograph/issues/273)), you should be able to read the fragment conditionally in the parent component.
- `useImperativeReference` (et al) will receive a `onCompleted` parameter that will be executed when the mutation completes, and will be passed the mutation results. You can set this in state. This is probably a bad practice, and shouldn't be relied on.
4 changes: 3 additions & 1 deletion docs-website/docs/pagination.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This API is likely to get simplified substantially, as we support `@loadable` on

## Walk through

Define a client field (without `@component`) that returns an array of items and accepts `skip` and `limit` parameters. It can accept other params:
First, define a client field (without `@component`) that returns an array of items and accepts `skip` and `limit` parameters. It can accept other params:

```tsx
import { iso } from '@iso';
Expand Down Expand Up @@ -77,3 +77,5 @@ export const PetDetailDeferredRouteComponent = iso(`
);
});
```

You can also use `useConnectionSpecPagination` if your connection field conforms to the [Relay connection spec](https://facebook.github.io/relay/graphql/connections.htm).
2 changes: 1 addition & 1 deletion docs-website/docs/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,6 @@ Now, if you refresh, the UI will be divided into subcomponents and look exactly

Congratulations! You just built your first Isograph app.

Want more? Try extracting the sorted list of films into its own client field (no need to use `@component` for this one.) Use hooks in your components (they work!) Check out the [magic mutation fields](/docs/expose-field-directives/) documentation to learn about how Isograph lets you update your data.
Want more? Try extracting the sorted list of films into its own client field (no need to use `@component` for this one.) Use hooks in your components (they work!) Check out the [mutation](/docs/mutation/) documentation to learn about how Isograph lets you update your data.

Or, [join the Discord](https://discord.gg/qcHUxb6deQ)!
3 changes: 2 additions & 1 deletion docs-website/sidebars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ const sidebars: SidebarsConfig = {
'isograph-config',
'loadable-fields',
'pagination',
'refetching',
'mutation',
'expose-field-directives',
'refetching',
'isograph-rules',
'faq',
{
Expand Down
10 changes: 4 additions & 6 deletions libs/isograph-react/src/core/componentCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,11 @@ export function getOrCreateCachedComponent(
rootLink: fragmentReference.root,
});

const firstParameter = {
data,
parameters: fragmentReference.variables,
};

return readerWithRefetchQueries.readerArtifact.resolver(
firstParameter,
{
data,
parameters: fragmentReference.variables,
},
additionalRuntimeProps,
);
}
Expand Down

0 comments on commit dc7beae

Please sign in to comment.