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

Port official relay tutorial #420

Merged
merged 31 commits into from
Nov 10, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
fb7ce74
copy raw state of tutorial
zth Jan 7, 2023
682bb43
add disclaimer/credits about this being a fork of the official tutorial
zth Jan 7, 2023
81673d1
more work on porting tutorial contents
zth Jan 15, 2023
931d571
Convert tutorial up to and including Types, Interfaces, and Polymorphism
mbirkegaard Feb 6, 2023
f8bd148
Convert Refetchable Fragments
mbirkegaard Feb 6, 2023
07a0296
Convert connections and pagination up to useTransition
mbirkegaard Feb 6, 2023
3ffff94
Convert rest of connections-pagination chapter
mbirkegaard Apr 13, 2023
b40cd54
Convert mutations and updates up until adding comments
mbirkegaard Apr 13, 2023
beec5b7
Finish converting mutations-updates
mbirkegaard Apr 14, 2023
562655c
Add rudimentary usage og onCompleted/onError
mbirkegaard Apr 14, 2023
7be098f
Add optimistic UI for posting a comment
mbirkegaard Apr 14, 2023
ee69d62
Convert missed javascript
mbirkegaard Apr 19, 2023
15d2d4b
Update intro to reflect yarn and rescript usage
mbirkegaard Apr 19, 2023
3fa0c5e
Add numbers to tutorial files
mbirkegaard Jul 4, 2023
7e24ff2
Final pass on 1 and 2
mbirkegaard Jul 12, 2023
e585896
Fix numbering
mbirkegaard Jul 12, 2023
0ceca34
Final pass on query basics
mbirkegaard Jul 12, 2023
993ff0f
Add note about names
mbirkegaard Jul 13, 2023
deffea4
Use graphql formatting
mbirkegaard Jul 13, 2023
4138b0e
Final pass on fragments
mbirkegaard Jul 13, 2023
0b2af6d
Final pass on part 5 and 6
mbirkegaard Jul 17, 2023
1d1de7e
Final pass on interfaces-polymorphism
mbirkegaard Aug 7, 2023
2c404e8
Final pass on refetchable fragments
mbirkegaard Aug 11, 2023
25b8f54
Final pass on 9, up to useTransition
mbirkegaard Aug 11, 2023
1fc499e
Finish final pass on 9
mbirkegaard Sep 7, 2023
c0414f1
Use getConnectionNodes in first connections example
mbirkegaard Oct 8, 2023
190f16a
Finish section 10
mbirkegaard Oct 8, 2023
3a577e4
Correct reference to ReScript using nominal types
mbirkegaard Nov 9, 2023
903ac51
Finish type deep-dive
mbirkegaard Nov 10, 2023
2203c6c
Set up magic comments
mbirkegaard Nov 10, 2023
8485884
Fix a bit of high-lighting
mbirkegaard Nov 10, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions rescript-relay-documentation/docs/tutorial/arrays-lists.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
---
id: tutorial-arrays-lists
title: Arrays and Lists
sidebar_label: Arrays and Lists
---

:::info
This tutorial is forked from the [official Relay tutorial](https://relay.dev/docs/tutorial/intro/), and adapted to RescriptRelay. All the credit goes to the Relay team for writing the tutorial.
:::

# Arrays and Lists

So far we’ve only dealt with components that have a single instance of the components they’re composed from. For example, we’re only showing a single Newsfeed story, and within that story there’s just a single author with a single profile picture. Let’s look at how to handle more than one of something.

GraphQL includes support for arrays, which in GraphQL are called _lists._ A field can be not only a single scalar value but an array of them, or not only a single edge but an array of edges. The schema specifies whether each field is a list or not, but, oddly, the GraphQL query syntax doesn’t distinguish between selecting a singular field and selecting a list — a quirky exception to the design principle that a GraphQL response should have the same shape as the query.

Request:

```
query MyQuery {
viewer {
contacts { // List of edges
id // field on a single item
name
}
}
}
```

Response:

```
{
viewer: {
contacts: [ // array in response
{
id: "123",
name: "Chris",
},
{
id: "789",
name: "Sue",
}
]
}
}
```

As it happens, the schema in our example app has a `topStories` field that returns a list of Stories, as opposed to the `topStory` field we're currently using that returns just one.

To show multiple stories on our newsfeed, we just need to modify `Newsfeed.res` to use `topStories`.

### Step 1 — Select a list in the fragment

Open `Newsfeed.res` and find `NewsfeedQuery`. Replace `topStory` with `topStories`.

```rescript
const NewsfeedQuery = graphql`
query NewsfeedQuery {
// change-line
topStories {
...StoryFragment
}
}
`;
mbirkegaard marked this conversation as resolved.
Show resolved Hide resolved
```

### Step 2 — Map over the list in the component

In the `Newsfeed` component, `data.topStories` will now be an array of fragment refs, each of which can be passed to a `Story` child component to render that story:

```rescript
@react.component
let make = () => {
let data = NewsfeedQuery.use(~variables=(), ())

switch topStories {
| None => React.null
| Some(stories) =>
<div className="newsfeed">
{stories
->Belt.Array.keepMap(x => x)
->Belt.Array.map(story => <Story story={story.fragmentRefs} />)
mbirkegaard marked this conversation as resolved.
Show resolved Hide resolved
->React.array}
</div>
}
}
```

### Step 3 — Add a React key based on the node ID

At this point, you should see multiple stories on the screen! It's beginning to look like a proper newsfeed app.

![Multiple stories](/img/docs/tutorial/arrays-top-stories-screenshot.png)

However, we're also getting a React warning that we're rendering an array of components without [providing a key attribute](https://reactjs.org/docs/lists-and-keys.html).

![React missing key warning](/img/docs/tutorial/arrays-keys-warning-screenshot.png)

It's always important to heed this warning, and more specifically to base keys on the identity of the things being displayed, rather than simply their indices in the array. This allows React to handle reordering and deleting items from the list correctly, since it knows which items are which even if their order changes.

Luckily, GraphQL nodes generally have IDs. We can simply select the `id` field of `story` and use it as a key:

```rescript
module NewsfeedQuery = %relay(`
query NewsfeedQuery {
topStories {
// change-line
id
...StoryFragment
}
}
`)

@react.component
let make = () => {
let {topStories} = NewsfeedQuery.use(~variables=(), ())

switch topStories {
| None => React.null
| Some(stories) =>
<div className="newsfeed">
{stories
->Belt.Array.keepMap(x => x)
// change-line
->Belt.Array.map(story => <Story key={story.id} story={story.fragmentRefs} />)
->React.array}
</div>
}
}
```

With that, we've got a collection of Stories on the screen. It's worth pointing out that here we're mixing individual fields with fragment spreads in the same place in our query. This means that Newsfeed can read the fields it cares about (directly from `Newsfeed.use`) while Story can read the fields it cares about (via `StoryFragment.use`). The _same object_ both contains Newsfeed's selected field `id` and is also a fragment key for `StoryFragment`.

:::tip
GraphQL Lists are only the most basic way of dealing with collections of things. We’ll build on them to do pagination and infinite scrolling later in the tutorial, using a special system called Connections. You’ll want to use Connections in most situations where you have a collection of items — although you’ll still use GraphQL Lists as a building block.
:::

Onward!
Loading