Chain React-ion: React Fiber / React Suspense / React Query

Hello, and welcome to Chain React-ion, where we cover all things React. I'm Emma Genesen, and today we've got some exciting topics. First, we'll be exploring React Fiber, the React reconciliation algorithm introduced in React 16. Then we'll take a look at React Suspense - when to use it, how to use it, and maybe even why to use it. We’ll finish with React Query - a tool I'm particularly fond of. 

Today's React under-the-hood topic is React Fiber. Let's start with a little history. So Fiber was first developed at Facebook -  like all amazing things we love. Nothing bad has ever come out of Facebook. So Zucky gave us React Fiber - or more specifically, a team of engineers at Facebook led by Andrew Clark and Sebastian Markbage. React Fiber was released in September 2017 with React 16. But its impact is still being felt today. React Fiber paved the way for a lot of future improvements to the React Library, including the upcoming "use" hook.

For those of you who don't know, the React library, and we're talking about React, not ReactDOM, is the React Library's core responsibility is the creation, management and reconciliation of the virtual DOM with the actual DOM. ReactDOM is responsible for rendering the content to the browser, but we’re focusing on React itself today, not it’s accompanying renderer.

The DOM is a tree of html elements - and the virtual DOM is a representation of the DOM but instead of DOM nodes, it's plain JS objects. Which brings us to Fiber: Fiber is the way React reconciles changes between the virtual and actual DOM.

Before Fiber, React used a stack for its reconciliation. This had a few limitations - for starters, there was no delineation between types of work or their import. React would process things as they came, and there was no reaching around the stack to bump up high priority items.

But with Fiber, React chunks up the work of updating the UI into smaller, manageable chunks that can be spread out over multiple frames. This allows the browser to handle other tasks, such as rendering, in between React updates, making the overall process more smooth and responsive. And each of these chunks, or units of work, can be reused. And we call these reusable units of work, Fibers.

Fibers can be scheduled and prioritized, which means that high priority updates, like a button press or input value change, can be prioritized over something less visually important to the user. What I find really cool about Fibers is that they can be paused, aborted and reused. So if a high priority update comes in, React can pause its work and handle that update first.

It uses a queue to handle incoming tasks, which provides a lot more flexibility than a stack. Now, the last thing I want to touch on is time slicing - which was a new concept in React 16. Instead of batching all updates in a single frame, work can be spread out over multiple frames, which keeps the UI from freezing or becoming unresponsive.

When a component needs to update, React creates a fiber for that component and adds it to a queue. React then processes the fibers in the queue one at a time. As it processes each fiber, it checks to see if there are any higher priority fibers that need to be processed first. If there are, React will pause the current fiber, save its state, and switch to the higher priority fiber. Once the higher priority fibers have been processed, React will return to the paused fiber and continue where it left off.

Ultimately, Fiber was a total rewrite of the old reconciler to enable more flexibility in core react's development.

Section II: React Suspense

Alright, let's move on to the next topic today - React Suspense! Is the suspense just killing you? Or maybe it was that joke? 

Suspense is a feature in React that allows devs to handle the loading state of asynchronous data in a more declarative way. When a component needs to load some asynchronous data, it can use the React.Suspense component to wrap the code that needs that data. This tells React that the component is "suspending" and that it should display a loading state or a fallback component while the data is being loaded.

When the data is ready, React will automatically re-render the component and the component will continue rendering as normal. The component will receive the data as a prop and can use it to render the final UI.

React Suspense also allows developers to specify a priority for loading different data. This means that if a higher-priority piece of data is being loaded, React can pause the loading of lower-priority data and focus on the higher-priority data first. This can make the user experience feel more responsive, as important data is loaded first.

React Suspense works with other async features such as server side rendering and data-fetching. It allows developers to specify a <Suspense> component that wraps the component tree that may need async data. When the component tree starts rendering, it will look for data that is not yet available and suspend the rendering of that component until the data is available.

Let's just move on to our final topic for the day, React Query!

React Query, for those who don't know, is a library for fetching, caching, and syncing server-side data that can eliminate tired boilerplate code - and maybe even the need for a front-end state management library all together.

The library was developed by open Sourcer Tanner Linsley in late 2019, and takes the good parts of Apollo and brings them to REST.

Many of our listeners may be familiar with the most basic data fetching pattern in React - use the browser’s fetch api to make a request in a useEffect on mount, then store the data in a slice of state. While this approach works for simple tasks, the second you want to introduce more robust features, like retries, error handling, caching and cache invalidation, the required lines of code grow rapidly.

That’s where React Query comes in. Out of the box, React Query offers caching and error handling, plus a bunch of advanced features that can easily be opted into. Refetch on window focus? Check. Optimistic updates when writing to a server? Check. An extensive suite of testing tools - you betcha.

React Query was developed to improve React’s ability to work with server-side state. Server-side state differs from local, or client, state, because it persists remotely out of your control, requires asynchronous actions to fetch and update, and can become out of date without the client being made aware. By caching requests, deduping multiple requests, updating data in the background, and memoizing query results, React Query streamlines the interactions between client and server with a unified interface for error handling, fetching, loading and more.

To set up React Query, you just pass an instantiated query client to a query client provider. This Provider-consumer model will be very recognizable to practiced React users. The two hooks that provide the majority of React Query’s functionality are useQuery and useMutation. The interface is really sleek - call useQuery with a string as a key, and a fetcher function, and useQuery returns the data, loading status, refetch functions and more.

UseMutation is designed for manipulating and updating data from the server. It works in a similar fashion, but instead of the hook directly returning data, you can also return mutateAsync, an async version of the fetcher function that can be called at a later place in your code. This is useful if you want to use the mutating function outside of a hook, say, in a function returned from your hook.

Here’s a few pro tips when using React Query:

In an ideal world, all data returned from a server will look how the frontend needs it to look. For the real world, if you need to transform your data, do so in the query function you provide to useQuery. It won’t provide a different experience in the slightest.

If you store your query results in some local state, you lose all the caching, refetching and validation benefits built into React Query. Why are you even using it, at that point?

If you’re using Websockets with React Query, there’s a couple things you should know. First, make sure to set the staleTime option of your QueryClient to Infinity. This will cause React Query to fetch the data on load. Then, make sure that in your onmessage handler for your websocket, you use the incoming event data entities as your key, and call invalidateQueries on the query client with your query key as an argument. This avoids the problem of overpushing, as only entities that should be updated are invalidated.

Lastly, you don’t have to solely rely on REST when using React Query. gRPC, for example, is a great use case with React Query. We’ll include an article outlining how Polar Signals used gRPC to create a performant backend, and gRPC-web with React Query for an elegant and scalable data synchronization solution.

In my opinion, if your application data lives on a server, just use React Query. Don't bother loading it into global state management systems like Redux and trying to manage it yourself. Even Dan Abramov agrees Redux is dead, long live Query.

If you have any questions or feedback about React Fiber, feel free to reach out to us through our website or social media. Thanks for listening!



Previous
Previous

Streaming Protocols - Tech Talk

Next
Next

OverVue Getting Started Guide