Dynamically Import React Components with React.lazy and Suspense

Kent C. Dodds
InstructorKent C. Dodds

Share this video with your friends

Send Tweet

With React 16.6.0, React Suspense was officially released as a stable feature (with limited support for React.lazy). Let's refactor our lazily-loaded components that are using react-loadable to components that use the built-in React.lazy feature.

Instructor: [00:00] Here, we're using React Loadable to load different parts of our application. We've got the home and we've got the user page. Then we can just render those components as they are with the reach/router. When they're ready, they'll be rendered.

[00:13] React actually has built in the capability to do lots of what Loadable is doing for us. Here, we're using React Loadable to dynamically import our home and user pages so that we can leverage code-splitting and make our users download less of our application all at once.

[00:29] Here, we're rendering those home and user components inside of our reach/router. Let's go ahead and refactor our React Loadable usage to use React Lazy, which is built into React.

[00:40] Instead of home being assigned to Loadable, we're going to say home is assigned to React.lazy of that same import. We'll do the same thing for our user -- React.lazy of that import.

[00:56] We can get rid of Loadable right here. If we save this and go over here, we're going to get a big error that says "A component suspended while rendering but no fallback UI was specified." We need to add a Suspense fallback component higher in the tree to provide a loading indicator or some sort of placeholder to display.

[01:13] What we need to do is, I'm going to pull in Suspense from React. Down here, we'll put this right below our error boundary and we'll stick the router inside of our Suspense right there.

[01:24] Then we'll provide a fallback. The fallback UI that we want to provide is the same thing that we had before, except we don't need to deal with this error state anymore, because this is actually the way that Suspense will work. If an error is thrown, then Suspense will throw it and our error boundary will catch it.

[01:40] We'll take this loading-message page as our fallback and stick it right there. Then we can get rid of this loading fallback as well. We'll save that and now we're loading our stuff. If I refresh, then I can see can't see dots, and I go and I get my loading data for can't see dots.

[01:55] In review, mostly what we did is just remove a whole bunch of code. We're importing Suspense and using React.lazy. Then we provide our Suspense right here, somewhere above where we're using React.lazy components and we're providing a fallback for the loading-message page of Loading Application.

Rahil
Rahil
~ 2 years ago

HI Kent .. An awesome course indeed. one doubt: what if while a preload request is being made the user actually navigates to that resource. will there be two calls from the browser or how will that be handled. Also, is it preferred to use hooks over render props if the render props weren't used earlier. (in my code ๐Ÿ™‚)

Rahil
Rahil
~ 2 years ago

HI Kent .. An awesome course indeed. one doubt: what if while a preload request is being made the user actually navigates to that resource. will there be two calls from the browser or how will that be handled. Also, is it preferred to use hooks over render props if the render props weren't used earlier. (in my code ๐Ÿ™‚)

Kent C. Dodds
Kent C. Doddsinstructor
~ 2 years ago

Hi Rahil,

Yes, there will be two requests, but depending on the implementation of the requesting code you may be able to cancel the first. Everything but React.lazy with suspense is extremely experimental so lots of things are still up in the air.

Horizon
Horizon
~ 2 years ago

I found the explanation of getting rid of error in LoadingFallback at 1:30 to be quite confusing. I had to go a look up how react-loadable works to understand that react-loadable passes an error to the loading component instead of throwing, which is what I guess React.lazy must do.

Jochem Nabuurs
Jochem Nabuurs
~ 2 years ago

Hi Kent,

I've got a project that involves 5 websites that are almost identical. They only have some styling differences, and some components aren't used on all websites but on just a few. I'm using create-react-app to create the basis of this project. I was thinking about the architecture for this project.

My initial setup was my main entry point being /src/index.tsx, where there was a switch statement inside the <App /> component that loaded a <WebsiteA /> or <WebsiteB /> component, depending on a env variable.

But as this resulted in a final build package that includes all code for all websites, this isn't a viable option.

Then i thought about creating a dynamic import that dynamically imports the app code based on the env var like this:

const site = process.env.REACT_APP_SITE
const App = React.lazy(() => import(`./src/sites/${site}/App`))
const 
ReactDOM.render(<Suspense
            fallback={
              <LoadingAppScreen />
            }
          ><App /></Suspense>, document.getElementById('root'))

This works in a way that webpack creates a build which only contains the code used. But I'm not sure if there are major drawbacks to this approach.

Kent C. Dodds
Kent C. Doddsinstructor
~ 2 years ago

Jochem, that would be a great question for my AMA: https://kcd.im/ama

Jochem Nabuurs
Jochem Nabuurs
~ 2 years ago

Cool. Didn't know about it's existence. Will drop a line there.

Met vriendelijke groet,

Jochem NabuursJaffiro Turennesingel 144 | 6663 GV Lent | M 06 52 09 10 09 | www.jaffiro.nl

Member of Dutch Web Alliance - Leaders in web technology | www.dutchweballiance.nl

Op vr 15 feb. 2019 om 15:20 schreef Kent notifications@egghead.io: