Browse Source

[Beta] lazy API (#5069)

main
dan 3 years ago
committed by GitHub
parent
commit
954ef33745
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      beta/src/content/apis/react/Suspense.md
  2. 202
      beta/src/content/apis/react/lazy.md
  3. 3
      beta/src/sidebarReference.json

6
beta/src/content/apis/react/Suspense.md

@ -95,11 +95,11 @@ The sequence will be:
--- ---
### Lazy loading components with Suspense {/*suspense-for-code-splitting*/} ### Lazy-loading components with Suspense {/*lazy-loading-components-with-suspense*/}
The [`lazy`](/apis/react/lazy) API is powered by Suspense. When you render a component imported with `lazy`, it will suspend if it hasn't loaded yet. This allows you to display a loading indicator while your component's code is loading. The [`lazy`](/apis/react/lazy) API is powered by Suspense. When you render a component imported with `lazy`, it will suspend if it hasn't loaded yet. This allows you to display a loading indicator while your component's code is loading.
```js {3,12-14} ```js {3,12-15}
import { lazy, Suspense, useState } from 'react'; import { lazy, Suspense, useState } from 'react';
const MarkdownPreview = lazy(() => import('./MarkdownPreview.js')); const MarkdownPreview = lazy(() => import('./MarkdownPreview.js'));
@ -112,6 +112,7 @@ function MarkdownEditor() {
... ...
{showPreview && ( {showPreview && (
<Suspense fallback={<Loading />}> <Suspense fallback={<Loading />}>
<h2>Preview</h2>
<MarkdownPreview /> <MarkdownPreview />
</Suspense> </Suspense>
)} )}
@ -143,6 +144,7 @@ export default function MarkdownEditor() {
<hr /> <hr />
{showPreview && ( {showPreview && (
<Suspense fallback={<Loading />}> <Suspense fallback={<Loading />}>
<h2>Preview</h2>
<MarkdownPreview markdown={markdown} /> <MarkdownPreview markdown={markdown} />
</Suspense> </Suspense>
)} )}

202
beta/src/content/apis/react/lazy.md

@ -2,22 +2,206 @@
title: lazy title: lazy
--- ---
<Wip> <Intro>
This section is incomplete, please see the old docs for [lazy.](https://reactjs.org/docs/react-api.html#reactlazy) `lazy` lets you defer loading component's code until it is rendered for the first time.
</Wip> ```js
const SomeComponent = lazy(load)
```
</Intro>
<Intro> <InlineToc />
---
## Usage {/*usage*/}
### Lazy-loading components with Suspense {/*suspense-for-code-splitting*/}
`React.lazy()` lets you define a component that is loaded dynamically. This helps reduce the bundle size to delay loading components that aren’t used during the initial render. Usually, you import components with the static [`import`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) declaration:
```js ```js
// This component is loaded dynamically import MarkdownPreview from './MarkdownPreview.js';
const SomeComponent = React.lazy(() => import('./SomeComponent'));
``` ```
</Intro> To defer loading this component's code until it's rendered for the first time, replace this import with:
<InlineToc /> ```js
import { lazy } from 'react';
const MarkdownPreview = lazy(() => import('./MarkdownPreview.js'));
```
This code relies on [dynamic `import()`,](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import) which might require support from your bundler or framework.
Now that your component's code loads on demand, you also need to specify what should be displayed while it is loading. You can do this by wrapping the lazy component or any of its parents into a [`<Suspense>`](/apis/react/Suspense) boundary:
```js {1,4}
<Suspense fallback={<Loading />}>
<h2>Preview</h2>
<MarkdownPreview />
</Suspense>
```
In this example, the code for `MarkdownPreview` won't be loaded until you attempt to render it. If `MarkdownPreview` hasn't loaded yet, `Loading` will be shown in its place. Try ticking the checkbox:
<Sandpack>
```js App.js
import { useState, Suspense, lazy } from 'react';
import Loading from './Loading.js';
const MarkdownPreview = lazy(() => delayForDemo(import('./MarkdownPreview.js')));
export default function MarkdownEditor() {
const [showPreview, setShowPreview] = useState(false);
const [markdown, setMarkdown] = useState('Hello, **world**!');
return (
<>
<textarea value={markdown} onChange={e => setMarkdown(e.target.value)} />
<label>
<input type="checkbox" checked={showPreview} onChange={e => setShowPreview(e.target.checked)} />
Show preview
</label>
<hr />
{showPreview && (
<Suspense fallback={<Loading />}>
<h2>Preview</h2>
<MarkdownPreview markdown={markdown} />
</Suspense>
)}
</>
);
}
// Add a fixed delay so you can see the loading state
function delayForDemo(promise) {
return new Promise(resolve => {
setTimeout(resolve, 2000);
}).then(() => promise);
}
```
```js Loading.js
export default function Loading() {
return <p><i>Loading...</i></p>;
}
```
```js MarkdownPreview.js
import { Remarkable } from 'remarkable';
const md = new Remarkable();
export default function MarkdownPreview({ markdown }) {
return (
<div
className="content"
dangerouslySetInnerHTML={{__html: md.render(markdown)}}
/>
);
}
```
```json package.json hidden
{
"dependencies": {
"immer": "1.7.3",
"react": "latest",
"react-dom": "latest",
"react-scripts": "latest",
"remarkable": "2.0.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
```
```css
label {
display: block;
}
input, textarea {
margin-bottom: 10px;
}
body {
min-height: 200px;
}
```
</Sandpack>
This demo loads with an artificial delay. The next time you untick and tick the checkbox, `Preview` will be cached, so there will be no loading state displayed. To see the loading state again, click "Reset" on the sandbox.
[Learn more about managing loading states with Suspense.](/apis/react/Suspense)
---
## Reference {/*reference*/}
### `lazy(load)` {/*lazy*/}
Call `lazy` outside your components to declare a lazy-loaded React component:
```js
const MarkdownPreview = lazy(() => import('./MarkdownPreview.js'));
```
#### Parameters {/*parameters*/}
* `load`: A function that returns a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) or some other *thenable* (a Promise-like object with a `then` method). React will not call `load` until the first time you attempt to render the returned component. After React first calls `load`, it will wait for it to resolve, and then render the resolved value as a React component. Both the returned Promise and the Promise's resolved value will be cached, so React will not call `load` more than once. If the Promise rejects, React will `throw` the rejection reason to let the closest Error Boundary above handle it.
#### Returns {/*returns*/}
`lazy` returns a React component that you can render in your tree. While the code for the lazy component is still loading, attempting to render it will *suspend.* Use [`<Suspense>`](/apis/react/Suspense) to display a loading indicator while it's loading.
---
### `load` function {/*load*/}
#### Parameters {/*load-parameters*/}
`load` receives no parameters.
#### Returns {/*load-returns*/}
You need to return a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) or some other *thenable* (a Promise-like object with a `then` method). It needs to eventually resolve to a valid React component type, such as a function, [`memo`](/api/react/memo), or a [`forwardRef`](/api/react/forwardRef) component.
---
## Troubleshooting {/*troubleshooting*/}
### My `lazy` component's state gets reset unexpectedly {/*my-lazy-components-state-gets-reset-unexpectedly*/}
Do not declare `lazy` components *inside* other components:
```js {4-5}
import { lazy } from 'react';
function Editor() {
// 🔴 Bad: This will cause all state to be reset on re-renders
const MarkdownPreview = lazy(() => import('./MarkdownPreview.js'));
// ...
}
```
Instead, always declare them at the top level of your module:
```js {3-4}
import { lazy } from 'react';
// ✅ Good: Declare lazy components outside of your components
const MarkdownPreview = lazy(() => import('./MarkdownPreview.js'));
function Editor() {
// ...
}
```

3
beta/src/sidebarReference.json

@ -60,8 +60,7 @@
}, },
{ {
"title": "lazy", "title": "lazy",
"path": "/apis/react/lazy", "path": "/apis/react/lazy"
"wip": true
}, },
{ {
"title": "memo", "title": "memo",

Loading…
Cancel
Save