Skip to content

Commit 3fba5ae

Browse files
authored
Merge pull request #19 from msd-code-academy/10-suspense-for-data-fetching
Add workshop for data fetching
2 parents b6a308e + 88c8898 commit 3fba5ae

31 files changed

+9541
-0
lines changed
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
module.exports = {
2+
env: {
3+
'browser': true,
4+
'es6': true
5+
},
6+
extends: [
7+
'plugin:react/recommended',
8+
'prettier/@typescript-eslint',
9+
'plugin:prettier/recommended'
10+
],
11+
globals: {
12+
'Atomics': 'readonly',
13+
'SharedArrayBuffer': 'readonly'
14+
},
15+
parser: '@typescript-eslint/parser',
16+
parserOptions: {
17+
ecmaFeatures: {
18+
'jsx': true
19+
},
20+
ecmaVersion: 2018,
21+
sourceType: 'module'
22+
},
23+
plugins: [
24+
'react',
25+
'@typescript-eslint'
26+
],
27+
rules: {
28+
'react/prop-types': 0
29+
}
30+
};
+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
lerna-debug.log*
8+
9+
# Diagnostic reports (https://nodejs.org/api/report.html)
10+
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11+
12+
# Runtime data
13+
pids
14+
*.pid
15+
*.seed
16+
*.pid.lock
17+
18+
# Directory for instrumented libs generated by jscoverage/JSCover
19+
lib-cov
20+
21+
# Coverage directory used by tools like istanbul
22+
coverage
23+
*.lcov
24+
25+
# nyc test coverage
26+
.nyc_output
27+
28+
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29+
.grunt
30+
31+
# Bower dependency directory (https://bower.io/)
32+
bower_components
33+
34+
# node-waf configuration
35+
.lock-wscript
36+
37+
# Compiled binary addons (https://nodejs.org/api/addons.html)
38+
build/Release
39+
40+
# Dependency directories
41+
node_modules/
42+
jspm_packages/
43+
44+
# TypeScript v1 declaration files
45+
typings/
46+
47+
# TypeScript cache
48+
*.tsbuildinfo
49+
50+
# Optional npm cache directory
51+
.npm
52+
53+
# Optional eslint cache
54+
.eslintcache
55+
56+
# Microbundle cache
57+
.rpt2_cache/
58+
.rts2_cache_cjs/
59+
.rts2_cache_es/
60+
.rts2_cache_umd/
61+
62+
# Optional REPL history
63+
.node_repl_history
64+
65+
# Output of 'npm pack'
66+
*.tgz
67+
68+
# Yarn Integrity file
69+
.yarn-integrity
70+
71+
# dotenv environment variables file
72+
.env
73+
.env.test
74+
75+
# parcel-bundler cache (https://parceljs.org/)
76+
.cache
77+
78+
# Next.js build output
79+
.next
80+
81+
# Nuxt.js build / generate output
82+
.nuxt
83+
dist
84+
85+
# Gatsby files
86+
.cache/
87+
# Comment in the public line in if your project uses Gatsby and *not* Next.js
88+
# https://nextjs.org/blog/next-9-1#public-directory-support
89+
# public
90+
91+
# vuepress build output
92+
.vuepress/dist
93+
94+
# Serverless directories
95+
.serverless/
96+
97+
# FuseBox cache
98+
.fusebox/
99+
100+
# DynamoDB Local files
101+
.dynamodb/
102+
103+
# TernJS port file
104+
.tern-port
105+
106+
*.solution.tsx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module.exports = {
2+
semi: true,
3+
trailingComma: 'all',
4+
singleQuote: true,
5+
printWidth: 120,
6+
tabWidth: 2,
7+
};

10-suspense-for-data-fetching/LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2020 Roman Klos
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
+153
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
# React Suspense for Data Fetching
2+
3+
## Prerequisites
4+
Please go to https://github.com/msd-code-academy/react-workshop and clone the project. Then inside the project run:
5+
6+
```
7+
git pull
8+
cd 10-suspense-for-data-fetching
9+
npm ci
10+
npm start
11+
```
12+
13+
We are using Parcel (https://parceljs.org/) for development server instead of webpack. It starts on port 1234. _(unfortunately hot reload sometimes doesn't work with parcel and react experimental, therefore is disabled)_.
14+
15+
## Introduction
16+
17+
- React Suspense for Data Fetching is pretty experimental, so there are some rough edges that you're going to be working through.
18+
- The APIs can change in the future.
19+
- Only Facebook is using it in production with Relay.
20+
- One of the things that I love about React in general, is that they're always applying the things that they're presenting to us internally at Facebook first, so they get some of the quirks worked out.
21+
- We're playing around with things, we're making little abstractions from things. Some of these things might be a bad idea, some of them might be good ideas. This is the whole point of this workshop, is to experiment with this, get familiar with the ideas and the concepts and the things that it enables, and then let you run off and play around with it, and give feedback back to the React team.
22+
23+
## Approaches
24+
25+
### Fetch-on-Render (not using Suspense)
26+
27+
Start rendering components. Each of these components may trigger data fetching in their effects and life-cycle methods. This approach often leads to “waterfalls”.
28+
29+
### Fetch-Then-Render (not using Suspense)
30+
31+
Start fetching all the data for the next screen as early as possible. When the data is ready, render the new screen. We can’t do anything until the data arrives.
32+
33+
1. Start fetching
34+
2. Finish fetching
35+
3. Start rendering
36+
37+
### Render-as-You-Fetch (using Suspense)
38+
39+
Start fetching all the required data for the next screen as early as possible, and start rendering the new screen immediately — before we get a network response. As data streams in, React retries rendering components that still need data until they’re all ready.
40+
41+
1. Start fetching
42+
2. Start rendering
43+
3. Finish fetching
44+
45+
## Setup
46+
47+
Install experimental React
48+
```
49+
npm install react@experimental
50+
```
51+
52+
Use concurrent mode to enable experimental features.
53+
54+
```
55+
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
56+
```
57+
58+
## Usage
59+
60+
```
61+
// This is not a Promise. It's a special object (resource) from our Suspense integration.
62+
const peopleResource = wrapPromise(fetchPeople);
63+
64+
const People = () => {
65+
// Try to read people list, although it might not have loaded yet
66+
const people = peopleResource.read();
67+
68+
return (
69+
<ul>
70+
{people.map((person, index) => (
71+
<li key={index}>{person.name}</li>
72+
))}
73+
</ul>
74+
);
75+
};
76+
77+
const Page = () => {
78+
return (
79+
<React.Suspense fallback={<div>Loading people...</div>}>
80+
<People />
81+
</React.Suspense>
82+
);
83+
};
84+
```
85+
86+
Here’s what happens when we render `<Page>` on the screen:
87+
88+
- We’ve already kicked off the requests in `peopleResource`. It gave us a special “resource” instead of a Promise. In a realistic example, it would be provided by our data library’s Suspense integration, like Relay.
89+
90+
- React tries to render `<Page>`. It returns `<People>` as children.
91+
92+
- React tries to render `<People>`. It calls `peopleResource.read()`. That will throw a Promise, no further code in `<People>` is executed. None of the data is fetched yet, so this component “suspends”. React skips over it, and tries rendering other components in the tree.
93+
94+
- There’s nothing left to try rendering. Because `<People>` suspended, React shows the closest `<Suspense>` fallback above it in the tree: "Loading people...". We’re done for now.
95+
96+
- This resource object represents the data that isn’t there yet, but might eventually get loaded. When we call `read()`, we either get the data, or the component “suspends”.
97+
98+
- When `peopleResource` is fetched, the `<People>` component will render successfully and we’ll no longer need the loading fallback. Eventually, we’ll get all the data, and there will be no fallbacks on the screen.
99+
100+
## Errors
101+
102+
When we need to handle our errors and display something useful to the user, then we can rely on an ErrorBoundary to catch that for us. This is the standard ErrorBoundary API that React has supported for quite some time.
103+
104+
```
105+
export default class ErrorBoundary extends React.Component {
106+
state = { error: null };
107+
108+
static getDerivedStateFromError(error) {
109+
return { error };
110+
}
111+
112+
componentDidCatch() {
113+
// log the error to the server
114+
}
115+
116+
render() {
117+
return this.state.error ? (
118+
<div>Error: {this.state.error}</div>
119+
) : (
120+
this.props.children
121+
);
122+
}
123+
}
124+
```
125+
126+
## Loading States
127+
128+
`useTransition` allows components to avoid undesirable loading states by waiting for content to load before transitioning to the next screen. It also allows components to defer slower, data fetching updates until subsequent renders so that more crucial updates can be rendered immediately.
129+
130+
The useTransition hook returns two values in an array.
131+
132+
- startTransition is a function that takes a callback. We can use it to tell React which state we want to defer.
133+
- isPending is a boolean. It’s React’s way of informing us whether we’re waiting for the transition to finish.
134+
135+
136+
## Suspense List
137+
138+
`<SuspenseList>` helps coordinate many components that can suspend by orchestrating the order in which these components are revealed to the user.
139+
140+
SuspenseList takes two props:
141+
142+
- revealOrder (forwards, backwards, together) defines the order in which the SuspenseList children should be revealed.
143+
144+
- together reveals all of them when they’re ready instead of one by one.
145+
146+
- tail (collapsed, hidden) dictates how unloaded items in a SuspenseList is shown.
147+
148+
- By default, SuspenseList will show all fallbacks in the list.
149+
- collapsed shows only the next fallback in the list.
150+
- hidden doesn’t show any unloaded items.
151+
152+
## Sources and Useful Links
153+
- https://reactjs.org/docs/concurrent-mode-suspense.html

0 commit comments

Comments
 (0)