- Suspense and Concurrent Mode help improve user experience when CPU or IO is lagging
- Also improves user experience when CPU/IO is fast (minimizes flash of loading spinners/jankiness which cause reflows and slower perceived user experience)
- Allows a web app to feel like a fast & fluid native app with smooth responsive transitions
- facebook is building v5 of their site on these features and it looks awesome
- experimental but already used in complex app that serves billions of users
- Initial impression = just makes loading logic a bit easier, but in a bizarre way (throwing promises)
- Much more than that
- Inversion of control (child declares data dependencies) keeps components isolated and modular
- Parent or previous screen is completely agnostic of data needs - whether the child/next screen needs to suspend waiting for code-split components to load, images, data, or other assets. Doesn't care. Enables loose-coupling
- Can be used separately from concurrent mode, but in conjunction with concurrent mode, it faciliates some interesting patterns that significantly improve user experience (such as remaining on current screen and staying responsive to user input while rendering next screen in memory (creating dom nodes and running user code in components))
- Similar to declarative error boundaries:
- https://jsfiddle.net/491na827/1/
- Code also in this repo
- error boundary will show in development. Click x on error overlay or escape
- or just run build version:
yarn run build && serve -s build
- Suspense Boundaries function like a try...catch:
- React DevTools let you toggle suspending state of components to test out your boundaries:
- Many names: async rendering, time slicing, and now concurrent mode
- UI frameworks today have to complete rendering on state change before responding to user input
- React Concurrent Mode keeps screen responsive
- Dan's original jsconf demo app
shift /
to bring up frame trackershift
click to mount/unmount chart
- Dan's original jsconf demo app
- Splits up rendering into units of work and intermittently yields back to browser to handle user events:
- Think of it like a git branch. It works on new UI changes in memory and only commits to "master" (the actual DOM) when all the changes are ready
- babel converts your JSX expressions at build time to virtual dom elements, react reconciles/diffs the virtual dom elements with actual dom nodes, and then commits the changes to dom
- You may have heard this word "fiber" before. Before "fiber" architecture, React would recursively render all your virtual dom elements and their children. Rendering was not interruptible because each recursive call got pushed to call stack and they all had to run at once:
- Fiber architecture introduced a "fiber" data structure that allows react to render converted JSX elements in same order as recursive algorithm, but in a while loop that is interruptible so react can intermittently yield back to browser to stay responsive:
- DEMO interruptible fiber algorithm
- code also in repo in fiber-demo folder
- Fibers loosely-correspond to components and have pointers to child, parent, and sibling (like a linked list)
- Fibers also enable stateful functional components because react internally associates your
useState
anduseEffect
hooks with the currently rendering fiber
- Just scratching the surface here...
- Remaining on current screen and staying responsive to user input while rendering next UI state in memory
- Without concurrent mode:
- Render before IO/data returns = worse perceived loading
- Render after data returns = worse actual loading due to IO->CPU waterfall (wasted cycles while waiting for data to return)
- Keep type-ahead responsive without debouncing or throttling
- Helps facilitate patterns of fetching data, code, assets in parallel and preventing waterfalls
- Facebook's v5 components declare data dependencies in GraphQL fragments
- Relay aggregates these fragments at build time into top-level queries
- can defer/stream lower-priority data and load above the fold content first so lower-priority data does not suspend transition
- build time step allows parents to stay agnostic of child component's data needs and faciliates loose-coupling/reusability
- Suspense/concurrent mode keep everything loading in smoothly and in intentional order
- components do not initiate the fetching of data (this leads to waterfalls)
- Route change or tab switch or button events, etc. will trigger fetching of data
- REST proof of concept:
- Splitting low/high priority updates allows better perceived user experience:
- Many of these patterns would be difficult if not impossible without concurrent mode and suspense
- Are some of these patterns only possible with Virtual Dom?
- Not sure compilers like Svelte can achieve such patterns when state changes mutate dom immediately
- concurrent svelte issue
- suspense svelte issue
We can probably avoid throwing Promises, but I think we need to at minimum execute the children without showing them if necessary. This makes everything more complicated like conditional rendering in between the Suspense placeholder and the asynchronous call. But that's the problem we need to solve.Thats what makes this interesting.
- Rich Harris (creator of Svelte) has some compelling critiques of React's virtual dom model and its leaky functional abstractions layered on inherently stateful mutable browser dom
- Virtual dom allows React to do some interesting things (concurrent mode, same JSX patterns reconciling with different UI primitives (react native), etc.)
- This is one of the most interesting debates for the future of frontend frameworks/compilers and is something to keep an eye on:
- https://reactjs.org/docs/concurrent-mode-intro.html
- I easily spent probably an entire weekend reading and rereading parts of these. Lot of concepts and might not be immediately clear how they all relate to each other until you try to write or imagine those patterns without concurrent mode and suspense
- Read the blog post too! They say its only for library authors and not application developers, but a lot of implications didn't click for me until I read the blog post:
- Relay Hooks experimental (only data fetching library currently compatible with concurrent mode)