8

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.

7
  • The import path is good, and yes it does default export the component. Commented Feb 21, 2020 at 16:34
  • 1
    Thanks for your feedback regarding the wording in the docs. I've raised a pull request to change the wording and possibly open up a conversation: github.com/reactjs/reactjs.org/pull/2768 Commented Feb 21, 2020 at 16:47
  • 2
    @Adam Great feedback, thanks. I've included a note in the PR about possibly changing the wording to make it clear that one or the other is required. I'll push a new commit with some additional wording to make it clearer. Commented Feb 21, 2020 at 16:56
  • 1
    I've changed the wording in the PR to read "The lazy component must then be rendered inside a Suspense component or an alternative,". If you have ideas for a clearer way to describe this I'm open to the conversation. Commented Feb 21, 2020 at 17:00
  • 1
    @JamieDixon No, I think that works. Thanks for opening the PR I think it could help some people like me down the line. Commented Feb 21, 2020 at 17:01

1 Answer 1

11

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.

Sign up to request clarification or add additional context in comments.

4 Comments

Can you give some details on what Suspense is/does?
Thanks, I did figure this out (see my edit) however the docs indicate "should" be wrapped in suspense, which would indicate it is optional, not required. Maybe just a slight wording thing that should be fixed.
I assume you can't use Suspense when rendering server side? And its default react?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.