The findings from the Human-Computer Interaction research into real UIs.
About Speaker
Muhammad Omer Khan
Software Engineer @ KoderLabs
Open Source
Once our code starts rendering an update, including creating new DOM nodes and running the code inside components, they can’t interrupt this work. We’ll call this approach “blocking rendering”.
In Concurrent Mode, rendering is not blocking. It is interruptible. This improves the user experience. It also unlocks new features that weren’t possible before.
In Concurrent Mode, we can tell React to keep showing the old screen, fully interactive, with an inline loading indicator. React starts preparing the new screen in memory first, And when the new screen is ready, React can take us to it.
In Concurrent Mode, React can work on several state updates concurrently — just like branches let different team members work independently.
const ProfilePage = React.lazy(() => import('./ProfilePage')); // Lazy-loaded
// Show a spinner while the profile is loading
<Suspense fallback={<Spinner />}>
<ProfilePage />
</Suspense>
Suspense With Lazy Loading
Suspense for Data Fetching is a new feature that lets you also use <Suspense> to declaratively “wait” for anything else, including data.
What Suspense is not?
1. It is not a data fetching implementation
2. It is not a ready-to-use client.
3. It does not couple data fetching to the view layer.
What Suspense Lets you Do
1. It lets data fetching libraries deeply integrate with React.
2. It lets you orchestrate intentionally designed loading states.
3. It helps you avoid race conditions.
Before
Fetch-on-render:
Start rendering components. Each of these components may trigger data fetching in their effects and lifecycle methods. This approch often leads to "waterfalls".
(For Example, fetch in useEffect | componentDidMount)
Render-as-you-fetch:
Start fetching all the required data for the next screen as early as possible, and start rendering the new screen immediately.
Now
Race conditions are bugs that happen due to incorrect assumptions about the order in which our code may run. Fetching data in the useEffect Hook or in call lifecycle methods like componentDidUpdate often leads to them. Suspense can help here, too
// Error boundaries currently have to be classes.
class ErrorBoundary extends React.Component {
state = { hasError: false, error: null };
static getDerivedStateFromError(error) {
return {
hasError: true,
error
};
}
render() {
if (this.state.hasError) {
return this.props.fallback;
}
return this.props.children;
}
}
function ProfilePage() {
return (
<Suspense fallback={<h1>Loading profile...</h1>}>
<ProfileDetails />
<ErrorBoundary fallback={<h2>Could not fetch posts.</h2>}>
<Suspense fallback={<h1>Loading posts...</h1>}>
<ProfileTimeline />
</Suspense>
</ErrorBoundary>
</Suspense>
);
}
Using ErrorBoundary
function App() {
const [resource, setResource] = useState(initialResource);
const [startTransition, isPending] = useTransition({
timeoutMs: 3000
});
// ...
<button
onClick={() => {
startTransition(() => {
const nextUserId = getNextId(resource.userId);
setResource(fetchProfileData(nextUserId));
});
}}
>
function ProfilePage({ resource }) {
return (
<SuspenseList revealOrder="forwards">
<ProfileDetails resource={resource} />
<Suspense fallback={<h2>Loading posts...</h2>}>
<ProfileTimeline resource={resource} />
</Suspense>
<Suspense fallback={<h2>Loading fun facts...</h2>}>
<ProfileTrivia resource={resource} />
</Suspense>
</SuspenseList>
);
}
Installation
npm install react@experimental react-dom@experimental
Index.js
import ReactDOM from 'react-dom';
// If you previously had:
//
// ReactDOM.render(<App />, document.getElementById('root'));
//
// You can opt into Concurrent Mode by writing:
ReactDOM.createRoot(
document.getElementById('root')
).render(<App />);
Resources:
Slides Link
Code Link