Skip to content

Adding Learn docs for RSCs #6502

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

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
206 changes: 206 additions & 0 deletions src/content/learn/fetching-data.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
---
title: Fetching Data
---

<Intro>

The data rendered in your React app may come from a variety of sources and in many cases, your components may need to "wait" for the data to be fetched before they can render.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The data rendered in your React app may come from a variety of sources and in many cases, your components may need to "wait" for the data to be fetched before they can render.
The data rendered in your React app may come from a variety of sources, and in many cases, your components may need to wait for the data to be fetched before they can render.


In this chapter, we will go over common data sources and how to fetch and use that data in your React components.

</Intro>

<YouWillLearn>

* Static and dynamic data and how it affects data access
* How to fetch data in your components and examples of common data sources
* How components "wait" for data to be fetched
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* How components "wait" for data to be fetched
* How components wait for data to be fetched


</YouWillLearn>

## Static and Dynamic Data {/*static-and-dynamic-data*/}

In our previous React examples, we've used local variables to hold data that our components use.

```js
const ESTABLISHED_YEAR = 1986;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a good year


function CompanyFooter() {
return <p>Quality since {ESTABLISHED_YEAR}</p>;
}
```
In this component, the data is the year we store in the local variable `ESTABLISHED_YEAR`.

Our app may also use ESTABLISHED_YEAR in other places so we can decouple the data from our UI and provide the data in a separate module.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Our app may also use ESTABLISHED_YEAR in other places so we can decouple the data from our UI and provide the data in a separate module.
Since `ESTABLISHED_YEAR` is a variable, and not just information that's directly written in this component, we can re-use the same information in multiple components. We can also define `ESTABLISHED_YEAR` in a separate module and import it.


```js
import ESTABLISHED_YEAR from "./data";

function CompanyFooter() {
return <p>Quality since {ESTABLISHED_YEAR}</p>;
}
```

In both of these examples, the data is set and read once for `CompanyFooter`. The examples read the data outside of the component function so every-time the component is rendered, it refers to the same data.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a place earlier in the docs where we talk about things being defined in the module vs in the component render? If so it might be good to link back to it or to use very similar language here to refer back to the same concept. (I don't see any place that we talk about this, but it seems like something that should be explained clearly so I'm surprised.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
In both of these examples, the data is set and read once for `CompanyFooter`. The examples read the data outside of the component function so every-time the component is rendered, it refers to the same data.
In both of these examples, the value of `ESTABLISHED_YEAR` is fixed at the time the module for our component is first imported. It's defined outside of our component, so it can't change when the component is re-rendered.


This works because the nature of the data in this example is unchanging. An establishment year for a company is a historical record that will not be updated in the future. We refer to this type of data as **static**.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This works because the nature of the data in this example is unchanging. An establishment year for a company is a historical record that will not be updated in the future. We refer to this type of data as **static**.
This works because the data is unchanginga company's year of establishment will never be different in the future. We refer to this type of data as **static**.


On the other hand, there may be data that is ever-changing or unique to a user or location. Consider a Weather app that renders the temperature of a local city, the temperature data powering that app will frequently update. We refer to this type of data as **dynamic** data.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
On the other hand, there may be data that is ever-changing or unique to a user or location. Consider a Weather app that renders the temperature of a local city, the temperature data powering that app will frequently update. We refer to this type of data as **dynamic** data.
But a lot of data is changeful, or unique to a specific user or location. For example, if you're writing a weather app, you need to show the current temperature, and that is always changing. We refer to this type of data as **dynamic** data.


Generally, the labels "static" and "dynamic" refer to guarantees we can make about the data and who can access it. Static data usually means the data does not change often (yearly, monthly) or that its update schedule is predictable or controlled. As well, it can mean that the data is the same no matter who is asking for it.

Dynamic data may be updated by the minute or by users, which is unpredictable. It may also be unique to a certain user or locale-specific.

<Illustration>
Illustration of static, dynamic data. Statically, we can use an example of a train timetable schedule.
</Illustration>

The reason why we distinguish static and dynamic data is it affects how often we need to fetch the data. For dynamic data, we want to display current, "fresh", data to our users. To do this, we'll need to re-fetch the data periodically in case of updates. One way to do this is to fetch data in a component function.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall this section feels a bit in-the-weeds to me. We haven't even fetched any data, but we're having a semantic discussion about refreshing it. And speaking of semantic discussions... it says above that static data includes data where the updates are "predictable or controlled". But even if predictable, we may still want to fetch the data in component render — the only time we don't is if we're OK with the data never being updated for the lifetime of the page or app, which could be arbitrarily long.


## Fetching data in a component {/*fetching-data-in-a-component*/}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we not want to have any kind of preample here about the fact that React components, when used with an appropriate framework, can render on the server?


Consider a temperature component in a weather app. In this example, we assume there is a local file that is continually updated with the current temperature in the city of Los Angeles.

```js
import { readFile } from "node:fs/promises";

async function LATemperature() {
const temp = await readFile("/path/to/temperature");
return <p>The current temperature in Los Angeles is: {temp}</p>;
}
```

During render, we read the temperature and render it in the component. We use the function `readFile` from the `fs` package to read the local file. Don't worry about the `await`, `async` keywords for now, we'll dive deeper into that in the next section. The important thing is that we are reading a data source during the render function. Every time the component renders, the data is read again.

An important thing to note is that the component doesn't know when the data has changed. The component must be told to re-render, and only then will it read "fresh" data.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
An important thing to note is that the component doesn't know when the data has changed. The component must be told to re-render, and only then will it read "fresh" data.
An important thing to note is that the component doesn't know when the data has changed. The component must be told to re-render, and only then will it read fresh data.


#### Fetching data from a database {/*fetching-data-from-a-database*/}

Beyond reading from a file, another common data source is a [database](https://en.wikipedia.org/wiki/Database). You can imagine a database like a collection of spreadsheets, with each individual spreadsheet being referred to as a "table". Just like a spreadsheet, a table contains column names and every entry in the table is a row of data.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a weird jump in audience. A minute ago we're assuming they understand readFile (files, file paths, node) and here we're assuming they've never heard of a database. Then a moment later we're throwing down some SQL without comment.


Imagine we have a table named 'weather' in a database, with the columns:
* `temp` - the temperature recorded
* `city` - the city the temperature was recorded
* `date` - the date of the recorded temperature

Here, we query our imaginary weather table in our component.

```js
import { Client } from "pg";
import connectionConfig from "./config";

const client = new Client(connectionConfig);
await client.connect();

async function LATemperature() {
const temp = await client.query(
`SELECT temp, date FROM weather WHERE city = 'Los Angeles' ORDER BY date DESC`,
);
return (
<p>
The current temperature in LA is: {temp}. Last updated {date}.
</p>
);
}
```
First, we establish our database connection and then we send our query to the database to get the temperature.

Databases are often queried through a database server. Database servers may be running locally on your machine, or may be accessible through a network. `connectionConfig` will contain the address of where the database server is running.

<Note>
Our example uses an external library, [node-postgres](https://node-postgres.com/) to access a fictional Postgres database. There are many other types of databases and JavaScript libraries to interface with them. The details don't matter in this case and the main point is to illustrate that data is often stored on remote database servers and roughly what that access looks like in a React component.
</Note>

#### Fetching data from an API {/*fetching-data-from-an-api*/}

Similar to database servers, there may be other services you can fetch data from. A common example is using a third-party service to access data you don't own or maintain. These third-party services will provide an API, an interface for you to structure your data request.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Similar to database servers, there may be other services you can fetch data from. A common example is using a third-party service to access data you don't own or maintain. These third-party services will provide an API, an interface for you to structure your data request.
Many apps will fetch data from an API, whether one you create yourself or a third-party API.


Here is an example of a weather service that offers an API to get the latest temperature data for Los Angeles. To get the temperature, the API requires us provide the longitude and latitude of the location we're interested in.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I understand the significance of the lat and long specifically being called out, but maybe it should be interpolated into the URL — is the significance of the URL what is being alluded to here? Then maybe something like "Many APIs provide a URL that you can fetch in order to retrieve their data, with specific URL query parameters providing inputs — here, the latitude and longitude of Los Angeles."


```js
async function LATemperature() {
const { temperature } = await fetch(
`https://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41&hourly=temperature_2m`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I appreciate your boldness in adding a new third party to the very short list.

);
return <p>The current temperature in Los Angeles is: {temp}.</p>;
}
```

An API service will usually provide documentation on how to make a request to their server and how the data is returned to you.

## Waiting for asynchronous data during render {/*waiting-for-data-during-render*/}

You'll notice that once we started to read data during render, we began to use the keywords `async` and `await` in our component. Let's dive deeper into what that means.

```js
import { readFile } from "node:fs/promises";

async function LATemperature() {
const tempPromise = readFile("/path/to/temperature");
const temp = await tempPromise;
return <p>The current temperature in LA is: {temp}</p>;
}
```

In our local file example, we imported the function `readFile` from a file streaming package. `readFile` is an asynchronous function that takes in a file path and returns the file contents. An asynchronous function means that JavaScript does not pause execution to wait for the function to finish. Instead, it returns a Promise, a container for its future return value.

An analogy to calling an asynchronous function is like taking your clothes to the dry cleaners. You've given the work to clean your clothes to someone else, leaving you free to do other things. The dry cleaners give you a ticket, a promise, that they will complete this work and notify you when they're done. When the work is done successfully, the Promise is considered resolved.

<Illustration>
Illustration of the parallel work while getting dry cleaning
</Illustration>

Now, imagine you are trying to render the component `LATemperature`. The first thing you do is kick off the asynchronous work of reading the temperature file and you've received `tempPromise` as a Promise. Now, you're free to do the next set of work. But wait, the next instruction requires you to know the temperature, yet all we have is a Promise. The Promise may or may not be ready with the actual temperature value.

Here is where `await` comes in. `await` is a keyword that is used before a Promise and tells JavaScript to pause running the current function and wait for the Promise to resolve.Then, when it is resolved, await will unwrap the Promise container to return the value. We use await on tempPromise to pause our component rendering until the file is read and receive the value returned.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Here is where `await` comes in. `await` is a keyword that is used before a Promise and tells JavaScript to pause running the current function and wait for the Promise to resolve.Then, when it is resolved, await will unwrap the Promise container to return the value. We use await on tempPromise to pause our component rendering until the file is read and receive the value returned.
Here is where `await` comes in. `await` is a keyword that is used before a Promise and tells JavaScript to pause running the current function and wait for the Promise to resolve. Then, when it is resolved, `await` will unwrap the Promise container to return the value. We use `await` on `tempPromise` to pause our component rendering until the file has been read. Once the process is done, we receive the value that was read from the file and assign it to the variable `temp`.


In our analogy, this would be similar to waiting at the dry cleaners for them to finish because the next chore on your todo list requires those clothes. Thus, preventing you from doing anything else in the meantime.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The analogy seems imperfect: This makes it sound like using await is equivalent to using a synchronous API that pauses the main thread.

Maybe you need to introduce another task that depends on the dry cleaning, like packing your suitcase for a trip. You can do other stuff in the meantime, but to finish packing your suitcase you need to get the dry cleaning back.


Our other examples of fetching data from a database server or making a network request to an API endpoint are also asynchronous operations that return Promises.


<DeepDive>

#### When to use await {/*when-to-use-await*/}

Generally, you'll want to kick off asynchronous work as early as possible so that you can maximize the amount of work you can do before needing to `await` the Promise returned.

Due to the simple nature of our component, our example needed to immediately await the asynchronous operation because the component render was blocked on that data.

Here is an example where we are able to kick off the asynchronous work and then do some synchronous work in the meantime, and then finally await the data just before it is used.

```js
async function HoveringButton({ userContext, label, lastTouchPoint }) {
// async operation
const localePromise = getLocale(userContext);

// expensive calculation
const offset = calculateNewOffset(lastTouchPoint);

return ...
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Example incomplete? It doesn't await anything.

}
```

</DeepDive>

You'll notice that our component function declarations now have an `async` prefix. This is also a JavaScript keyword and any function that uses `await` will need to be marked with `async`. If a function `await`s a Promise, it itself becomes asynchronous.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This paragraph maybe belongs in the main discussion.

Whenever you use await within a function, you have to add the async keyword to the beginning of the function.


Imagine being the parent function calling the `LATemperature` component function. When you call `LATemperature` and it gets to the instruction of `await`-ing `readFilePromise`, JavaScript execution returns to you and you receive a Promise that promises to give you the completed render of `LATemperature`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LATemperature is a component, not a normal function, so you wouldn't normally call it. Maybe the chaining aspect of promises should be explained with a non-component function: for example, you could create a function readTemperature that reads the file and then parses it and extracts the temperature as a number.


<DeepDive>

#### Why is dynamic data-fetching asynchronous vs. static data reads? {/*why-is-dynamic-data-fetching-asynchronous-vs-static-data-reads*/}

TODO: go into build-time and run-time data access

</DeepDive>

## Recap {/*recap*/}

* Data is the content we display in our React apps.
* Static data refers to data that changes infrequently or within your control.
* Dynamic data refers to data that changes frequently or unpredictably.
* For dynamic data, you'll likely want to read your data in your component function so that it reads "fresh" data for every render.
* Accessing during component render, is often an asynchronous operation.
* Asynchronous functions perform work asynchronously to its calling function. They return Promises, a container for its future return value.
* You can tell your React components to wait for an asynchronous operation by await-ing the Promise returned.
29 changes: 29 additions & 0 deletions src/content/learn/rendering-data.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
title: Rendering Data
---

<Intro>


Data is the content that your UI displays. Data can come from many different sources and have different expectations on how it changes. Data fetching also introduces non-deterministic (unpredictable) behavior like loading and errors that your UI should properly communicate. In this chapter you'll learn how to fetch and render data as well as coordinate how your users experience loading and error states.

</Intro>

<YouWillLearn isChapter={true}>

* [How to fetch data](/learn/fetching-data)

</YouWillLearn>


## Fetching Data {/*fetching-data*/}

TODO.

<LearnMore path="/learn/fetching-data">
TODO
</LearnMore>

## What's next? {/*whats-next*/}

Head over to [Adding Interactivity](/learn/adding-interactivity) to start reading this chapter page by page!
11 changes: 11 additions & 0 deletions src/sidebarLearn.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,17 @@
}
]
},
{
"title": "Rendering Data",
"path": "/learn/rendering-data",
"tags": [],
"routes": [
{
"title": "Fetching Data",
"path": "/learn/fetching-data"
}
]
},
{
"title": "Adding Interactivity",
"path": "/learn/adding-interactivity",
Expand Down