Reputation: 1144
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
Reputation: 55792
A lazy
component should be a descendent of a Suspense
and if you use Suspense
then you need to provide a fallback
:
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