1 of 28

Suspense for data fetching:

How to fetch

during render

Robert Balicki ◆ ex-Relay team @ Meta

@StatisticsFTW ◆ robert.balicki@gmail.com

@isograph/react-disposable-state

github.com/isographlabs/isograph

2 of 28

Goals

  • Make network calls during the render phase
  • Understand other times to fetch: in an effect and in advance
  • Understand React and Suspense

3 of 28

Non-goals

  • Refetching
  • Caching
  • Offscreen, hot reloading
  • Server components, use(...)

4 of 28

Agenda

  • Comparison of fetching strategies
  • When does React render components?
  • Need external cache
  • @isograph/react-disposable-state
  • Demo

5 of 28

Fetching in an effect

  • Doesn't integrate with suspense
  • Starts network requests last

6 of 28

Fetching during render

7 of 28

Fetching in advance

8 of 28

Fetch on click

Fetch on hover

During render

9 of 28

Fetching in�an effect

Fetching during render (lazy)

Fetching in advance

Perf

Worst

Medium

Best

Loading states

No suspense

Yes suspense

Yes suspense

DevEx for�app devs

Medium, you must handle loading states

Simple

Medium, requires

passing props

Network waterfalls? 😡

Yes

Yes

No

Hook implementation complexity

Simple

Hard

Medium

10 of 28

Network waterfalls

Inner Network Request

Outer Network Request

11 of 28

In effect

Render (lazy)

In advance (preload)

Perf

Worst

Medium

Best

Loading states

No suspense

Yes suspense

Yes suspense

DevEx for�app devs

Medium, you must handle loading states

Simple

Medium, requires

passing props

Network waterfalls? 😡

Yes

Yes

No

Hook implementation complexity

Simple

Hard

Medium

12 of 28

Why support fetch during render?

  • During render ⋙ during an effect
  • During render/effect is necessary if you don't know why a component is rendering
  • Necessary at the root of every page absent router integration

13 of 28

When does React render components?

14 of 28

Since React renders components for you

  • Hooks
  • State
  • Suspense

  • ...for data fetching?
  • How many times?

15 of 28

❗❗❗ Key points ❗❗❗

  • Any component can be rendered any number of times

16 of 28

Pre-commit behavior of hooks

17 of 28

No identity

  • Pre-commit, components have no sense of identity
  • If a component suspends before committing
  • Hooks re-instantiated (useState, useRef, useMemo, useCallback)

— useMemo documentation on react.dev

18 of 28

❗❗❗ Key points ❗❗❗

  • Pre-commit, you cannot rely on hooks to store information
  • Any component can be rendered any number of times

19 of 28

Use an external cache

  • Deduplicate identical requests
  • Ability to identify requests across renders
  • getOrCreate(requestId)

20 of 28

Clean up our network requests

  • Garbage collection
  • Close web sockets
  • Effects?

21 of 28

Effects phase

22 of 28

❗❗❗ Key points ❗❗❗

  • Any component can be rendered any number of times
  • Pre-commit, you cannot rely on hooks to store information.

  • A successful render will not always be followed by a commit.
  • You can't rely on an effect's cleanup function to clean up side effects made during the render phase.

23 of 28

What to do?

  • Commit => clear the temporary retain and transfer ownership
  • Component renders...
  • Set a timeout: create first temporary retain

RᴇꞯᴜᴇꜱᴛCᴀᴄʜᴇ(ɪᴅ)

Empty

CᴀᴄʜᴇIᴛᴇᴍ

◇ Temporary retains: 0

◇ Permanent retains: 1

Render 1

RᴇꞯᴜᴇꜱᴛCᴀᴄʜᴇ(ɪᴅ)

CᴀᴄʜᴇIᴛᴇᴍ

◇ Temporary retains: 1

◇ Permanent retains: 0

Render 1

24 of 28

What to do?

  • Renders again

  • Component commits
  • Clear other temporary retain, and transfer ownership
  • Keep other temporary retain

Post 1

(LOADING AUTHOR PHOTO)

Post 2

(LOADING AUTHOR PHOTO)

🎉

🎉

RᴇꞯᴜᴇꜱᴛCᴀᴄʜᴇ(ɪᴅ)

CᴀᴄʜᴇIᴛᴇᴍ

◇ Temporary retains: 2

◇ Permanent retains: 0

Render 1

Render 2

RᴇꞯᴜᴇꜱᴛCᴀᴄʜᴇ(ɪᴅ)

CᴀᴄʜᴇIᴛᴇᴍ

◇ Temporary retains: 1

◇ Permanent retains: 1

Render 1

Render 2

25 of 28

What to do?

  • Refetch when a new component renders

RᴇꞯᴜᴇꜱᴛCᴀᴄʜᴇ(ɪᴅ)

CᴀᴄʜᴇIᴛᴇᴍ

◇ Temporary retains: 0

◇ Permanent retains: 1

Component

RᴇꞯᴜᴇꜱᴛCᴀᴄʜᴇ(ɪᴅ)

Empty

CᴀᴄʜᴇIᴛᴇᴍ

◇ Temporary retains: 0

◇ Permanent retains: 1

Component

26 of 28

❗❗❗ Cache/Cache Item Behaviors❗❗❗

  • When making a network request, call getOrCreate
  • A pre-commit render results in a temporary retain
  • A render committing for the first time clears one temporary retain and creates a permanent retain
  • If there are no more temporary retains, the cache item is removed from the cache and used by existing components
  • If there are no more temporary or permanent retains, the underlying network request can be disposed

27 of 28

Code spelunking time

28 of 28

Suspense for data fetching:

How to fetch

during render

Robert Balicki ◆ ex-Relay team @ Meta

@StatisticsFTW ◆ robert.balicki@gmail.com