From b188555b0687962900a9e6358fddb63eabeea469 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= Date: Tue, 23 Oct 2018 16:51:54 -0700 Subject: [PATCH] Document React.lazy (#1284) * Document React.lazy Suspense is documented here since it's the only built-in way so far to use it. In the future it'll likely be documented on its own more like Error boundaries. This hoists React.lazy as the primary way to do code splitting. However, before getting into it we keep the recommendation to use react-loadable for server-side rendered apps since Suspense is not yet ready for the server. * Tweaks * Tweaks * nits --- content/docs/code-splitting.md | 152 ++++++++++++++++++++++++--------- 1 file changed, 113 insertions(+), 39 deletions(-) diff --git a/content/docs/code-splitting.md b/content/docs/code-splitting.md index 0ba1d06e..132fa99a 100644 --- a/content/docs/code-splitting.md +++ b/content/docs/code-splitting.md @@ -111,46 +111,107 @@ If you're setting up Webpack yourself, you'll probably want to read Webpack's When using [Babel](http://babeljs.io/), you'll need to make sure that Babel can parse the dynamic import syntax but is not transforming it. For that you will need [babel-plugin-syntax-dynamic-import](https://yarnpkg.com/en/package/babel-plugin-syntax-dynamic-import). -## Libraries +## `React.lazy` -### React Loadable +> Note: +> +> `React.lazy` and Suspense is not yet available for server-side rendering. If you want to do code-splitting in a server rendered app, we still recommend [React Loadable](https://github.com/thejameskyle/react-loadable). It has a nice [guide for bundle splitting with server-side rendering](https://github.com/thejameskyle/react-loadable#------------server-side-rendering). -[React Loadable](https://github.com/thejameskyle/react-loadable) wraps -dynamic imports in a nice, React-friendly API for introducing code -splitting into your app at a given component. +The `React.lazy` function lets you render an dynamic import as a regular component. **Before:** ```js import OtherComponent from './OtherComponent'; -const MyComponent = () => ( - -); +function MyComponent() { + return ( +
+ +
+ ); +} ``` **After:** ```js -import Loadable from 'react-loadable'; +const OtherComponent = React.lazy(() => import('./OtherComponent')); + +function MyComponent() { + return ( +
+ +
+ ); +} +``` -const LoadableOtherComponent = Loadable({ - loader: () => import('./OtherComponent'), - loading: () =>
Loading...
, -}); +This will automatically load the bundle containing the `OtherComponent` when this component gets rendered. + +`React.lazy` takes a function that must call a dynamic `import()`. This must return a `Promise` which resolves to a module with a `default` export containing a React component. + +### Suspense + +If the module containing the `OtherComponent` is not yet loaded by the time `MyComponent` renders, we must show some fallback content while we're waiting for it to load - such as a loading indicator. This is done using the `Suspense` component. + +```js +const OtherComponent = React.lazy(() => import('./OtherComponent')); + +function MyComponent() { + return ( +
+ Loading...
}> + + + + ); +} +``` + +The `fallback` prop accepts any React elements that you want to render while waiting for the component to load. You can place the `Suspense` component anywhere above the lazy component. You can even wrap multiple lazy components with a single `Suspense` component. + +```js +const OtherComponent = React.lazy(() => import('./OtherComponent')); +const AnotherComponent = React.lazy(() => import('./AnotherComponent')); + +function MyComponent() { + return ( +
+ Loading...
}> +
+ + +
+ + + ); +} +``` + +### Error boundaries + +If the other module fails to load (for example, due to network failure), it will trigger an error. You can handle these errors to show a nice user experience and manage recovery with [Error Boundaries](/docs/error-boundaries.html). Once you've created your Error Boundary, you can use it anywhere above your lazy components to display an error state when there's a network error. + +```js +import MyErrorBoundary from '/MyErrorBoundary'; +const OtherComponent = React.lazy(() => import('./OtherComponent')); +const AnotherComponent = React.lazy(() => import('./AnotherComponent')); const MyComponent = () => ( - +
+ + Loading...
}> +
+ + +
+ + + ); ``` -React Loadable helps you create -[loading states](https://github.com/thejameskyle/react-loadable#creating-a-great-loading-component), -[error states](https://github.com/thejameskyle/react-loadable#loading-error-states), -[timeouts](https://github.com/thejameskyle/react-loadable#timing-out-when-the-loader-is-taking-too-long), -[preloading](https://github.com/thejameskyle/react-loadable#preloading), and -more. It can even help you [server-side render](https://github.com/thejameskyle/react-loadable#------------server-side-rendering) an app with lots of code-splitting. - ## Route-based code splitting Deciding where in your app to introduce code splitting can be a bit tricky. You @@ -163,31 +224,44 @@ re-rendering the entire page at once so your users are unlikely to be interacting with other elements on the page at the same time. Here's an example of how to setup route-based code splitting into your app using -libraries like [React Router](https://reacttraining.com/react-router/) and -[React Loadable](https://github.com/thejameskyle/react-loadable). +libraries like [React Router](https://reacttraining.com/react-router/) with `React.lazy`. ```js import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'; -import Loadable from 'react-loadable'; +import React, { Suspense, lazy } from 'react'; -const Loading = () =>
Loading...
; - -const Home = Loadable({ - loader: () => import('./routes/Home'), - loading: Loading, -}); - -const About = Loadable({ - loader: () => import('./routes/About'), - loading: Loading, -}); +const Home = lazy(() => import('./routes/Home')); +const About = lazy(() => import('./routes/About')); const App = () => ( - - - - + Loading...}> + + + + + ); ``` + +## Named Exports + +`React.lazy` currently only supports default exports. If the module you want to import uses named exports, you can create an intermediate module that reexports it as the default. This ensures that treeshaking keeps working and that you don't pull in unused components. + +```js +// ManyComponents.js +export const MyComponent = /* ... */; +export const MyUnusedComponent = /* ... */; +``` + +```js +// MyComponent.js +export { MyComponent as default } from "./ManyComponents.js"; +``` + +```js +// MyApp.js +import React, { lazy } from 'react'; +const MyComponent = lazy(() => import("./MyComponent.js")); +```