React Podcast Episode 29 - 'Don't Rewrite Your App for Hooks and Suspense' with Jared Palmer
More exciting features coming to React discussed in React Podcast episode #29.
DISCLAIMER: Below is an unofficial show note highlighting discussions as I am not affiliated with podcast, just an avid listener :).
📑 Table of Contents
- Suspense + Cache
- Next Challenge
- Chantastic's Insight
- Upgrade Tips
- Don't rewrite your app at all
🔹 Fundamentally replaces Render Props
- Render Props can cause a callback hell caused by chaining multiple Render Props( prevalent in node).
- It's easy to test
- Everything is in scope
Sung's note: because class components would require you to access states using
this & many new people suffer from not knowing to bind methods with current instance's
Less code is required
- as it removes the need to create interfaces for props and states for class components.
- And with initial value of
useStateTypeScript can infer the type of state returned as the first argument in the returned tuple (helps autocomplete feature in editors).
- Makes it easy to move states around.
🔹 Other Benefits
- You can use plain function syntax without transpiling.
- Read Hooks documentation top to bottom without skipping
🔹 Possible drawback
Learning curve could be an issue because this is a new feature thus requires you to remember "bunch of stuff".
But once you get over the hurdle it's great because
- You can easily refactor states.
- You have a choice to put new state(s) in component or elsewhere.
Jared is optimistic about Hooks' future and considers it the next logical step to take for React.
🔹 What is it about?
Suspense is about an "efficient loading of resources".
🔹 Loading State Problem
On fast connection, users prefer not to see a janky loading screens but on a slow connection they'd like to see it.
Speed, performance, and UX (User Experience) directly conflict with each other in this case because
- According to a UX research, user do not perceive the speed difference when a change occurs in 200~400ms.
- Showing a loading spinner during that period makes user to feel it as janky on a fast connection but would like to know something is happening when the threshold is reached.
Showing nothing still doesn't solve a problem as you still have to check of the loading state.
Many attempts were made to address the issue (lazy load data, assets, etc)
but they still don't react to (slow/fast) network.
🔹 How does Suspense work?
Suspense allows you where to pause rendering while other components wait for resources.
When a resource arrives React can continue to render or else Suspense can show a fallback content.
Suspense literally stops time from happening.
🔹 How does it help?
Suspense enables us to orchestrate fallback declaratively.
- This enables us to remove loading states and imperative checks.
- You just tell React where to suspend/wait (DOM mutation is paused during this phase).
Suspense can make loading dynamic components easy.
- We can lazily load components using React.lazy by wrapping it in Suspense telling React to pause until the component is resolved.
- If it takes longer than a certain threshold, Suspense displays a fallback.
- This solves speed, performance, and UX issue mentioned in the problem statement above.
🔹 How to handle Suspense errors
Use Error Boundaries.
🔹 Concurrent Mode
Prerequisite is that your app needs to pass a strict mode by fixing all warning generated when you wrap your app using
🔹 What if you can't pass Strict Mode test?
You can still use Concurrent Mode by
- identifying React tree where strict mode passes
- and wrapping that portion of tree in React.ConcurrentMode.
This enables an incremental adoption.
🔹 Outside of Concurrent Mode?
If Suspense is used outside concurrent mode, Suspense will fallback right away while child component is still loading itself.
You still get janks but with less code as it rids of loading state used in imperative code.
Benefits of using Suspense outside of Concurrent Mode are
- A better DX (Developer Experience)
- and it slowly prepares your app for the better UX.
🌉 Suspense + 💲 Cache
Data fetching is simplified when combined with cache thanks to how Suspense works because everything (wasn't clear. Did Jared mean states? or components?) is always defined.
When everthing (?) is defined, TypeScript would not require partial classes (I need some feedback on this as I am a TS newbie...).
Coupling Hooks with Suspense + Cache, you can access asynchronous code as if it is a synchronous one providing a better readability.
This combination also solves waterfall issue of component state dependency orchestration (case where next components need to wait when previous ones are loaded).
Siblings of a single Suspense run in parallel.
In the code above, Component1 & 2 run in parallel.
Similar concept as how Promise.all runs promises in parallel.
Caveat: Nested components in Suspense are going to run with waterfall.
Sung's Note: Not sure how this works exactly...
And you can nest Suspense within each other infinitely.
🔹 Use Cases
- Using a low quality image place holder while a high res version is loading.
- Wrap regular image component in Suspense.
- Regular image will be fetched while Suspense displays a low quality image as a fallback until the high resolution resource becomes available in cache.
- Loading external 3rd party payment script.
- You can enable check out button only when Stripe script is loaded.
- You can load next component to load ahead of time to enable a transition effect.
- For SSR (server-side rendering), you have to know what data your components need ahead of time.
- This forces Apollo to traverse React tree twice (
Components have to figure out what data they need so React is going to render without data and then with data).
- This slows down the SSR page.
- In later cache implementations, React tree can be walked down only once but not yet "quite there, yet".
- This forces Apollo to traverse React tree twice (
It is just a reference implementation how it can hook into React.
Data normalization & validation features are missing (but coming).
Suspense is about efficiently orchestrating resources.
providing both good DX & UX
You can put anything (images, components, data, etc) in Cache
providing a better UX
React lets us describe "what" you want to do, not "how" you want to do.
Refer to the "declarative" loading states using Suspense.
🥊 Next Challenge
Animation is the next challenge and JP considers it's an unsolved problem.
🤔 Chantastic's Insight
React team is making "right thing easy" to do with Suspense's
maxDuration (the threshold time for the fallback).
It makes taking care of the
3rd state (network state where loading data takes long or short) between "loading" state and "data" state.
📈 Upgrade Tips
Incrementally adopt these new features, instead because React is always incrementally adoptable & improvable.
e.g.) Suspense can be used in concurrent mode for
- the whole app (with
- partially (using
- or outside concurrent mode
Focus on movement not migration.
React.lazyfor code splitting
- Manage assets using cache to orchestrate loading & caching resources
- Use Suspense + Cache to fetch and store data
⚠ WARNING: Wait on this tip because data normalization & validation features are missing (but coming).
🔹 Miscellaneous Tips
- Make a plan
- Budget available extra time.
- Refactor only when necessary and do not refactor everything at once.
🙅 Don't rewrite your app at all
Jared has stressed this 4 times at