Cal Irvine
Cal Irvine

Reputation: 1144

React.lazy not loading any components

I am trying to implement some code splitting in a React application. This isn't strictly necessary in this case as it's a rather small application, but I thought I would try this technique out in a low risk setting before implementing it in larger projects.

My code that works:

import React from 'react'
import { useUser } from './context/userContext'
import Layout from './components/layout'
import Routes from './components/routes'
import LandingScreen from './components/authApp/LandingScreen'

const App = () => {
  const user = useUser()

  return (
    <>
      {user ? (
        <Layout>
          <Routes />
        </Layout>
      ) : (
        <LandingScreen />
      )}
    </>
  )
}

export default App

If I try to change any components to use React.lazy, for instance

const LandingScreen = React.lazy(() => import('./components/authApp/landingScreen'))

My app compiles fine, but no components render in the browser, and I receive this error:

index.js:1 The above error occurred in one of your React components:
    in Unknown (at App.js:26)
    in App (at src/index.js:14)
    in UserProvider (at appProvider.js:7)
    in AuthProvider (at appProvider.js:6)
    in AppProviders (at src/index.js:13)

Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://reactjs.org/docs/error-boundaries.html to learn more about error boundaries.

I'd appreciate any clues to what I'm doing wrong here. The LandingScreen component just renders some divs and my login component, but the same error occurs even if it is just rendering a single div.'

Edit: Wrapping the lazy loaded component in a Suspense component fixes it. The docs seem to indicate that the Suspense component is optional, but maybe it's not? Anyone that knows more and can chime in, that would be appreciated.

Upvotes: 8

Views: 11340

Answers (1)

Adam Jenkins
Adam Jenkins

Reputation: 55792

A lazy component should be a descendent of a Suspense and if you use Suspense then you need to provide a fallback:

All right here in the docs

const App = () => {
  const user = useUser()

  return (
    <Suspense fallback={<></>}>
      {user ? (
        <Layout>
          <Routes />
        </Layout>
      ) : (
        <LandingScreen />
      )}
    </Suspense>
  )
}

Note: you don't have to use Suspense, but if you don't then you've essentially got to reinvent it with your own ErrorBoundary, so it's easier to just use it.

Susepense Gotcha

If any component beneath a Suspense suspends while rendering (e.g. is lazy) then the entire component tree up to the nearest Suspense unmounts and remounts when the component is ready. This can be a huge gotcha.

What is Suspense Actually Doing?

React has been throwing around this idea that a component can throw a Promise -throw is very important here, it can't return a Promise, it must throw it - to indicate it's "async". If the nearest ErrorBoundary (which Suspense is) catches a Promise, then it displays it's fallback prop. When the Promise it caught resolves, it renders it's children - tremendously simple when you think about what it's actually doing.

I say it's been playing around with the idea because, even though they've implemented it for Suspense, they're considering it as an API for data loading and, indeed, people have already shown that you can use it that way now (can't find the link right now, but it's super cool).

The API for data fetching is expected to be formalized sometime in the near future.

Upvotes: 11

Related Questions