Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: synchronous state updates in cacheable-sections and fix type generation #1391

Merged
merged 20 commits into from
Nov 26, 2024

Conversation

KaiVandivier
Copy link
Contributor

@KaiVandivier KaiVandivier commented Nov 22, 2024

Follow-up from #1387

Quick tip:

I merged in master to include some recent fixes to avoid unnecessary rerenders from useCacheableSection, which unfortunately clutters up the commit history -- for ease of browsing, this range of commits here includes the relevant test changes. A few small type changes and a bump for @dhis2/cli-app-scripts are not included in these though

The cause:

The failing tests were correctly identifying a change in behavior with React 18: state updates in callbacks were being handled more asynchronously due to batching, which created an unwanted race condition. This section in the linked the batching article had a solution:

What if I don’t want to batch?

Usually, batching is safe, but some code may depend on reading something from the DOM immediately after a state
change. For those use cases, you can use ReactDOM.flushSync() to opt out of batching:

import { flushSync } from 'react-dom'; // Note: react-dom, not react

function handleClick() {
  flushSync(() => {
    setCounter(c => c + 1);
  });
  // React has updated the DOM by now
  flushSync(() => {
    setFlag(f => !f);
  });
  // React has updated the DOM by now
}

We don't expect this to be common.

For some extra context on this hook:

While asynchronous behavior could work for useCacheableSection and the tests could be updated to handle that, the returned values from the hook are intended to give users the chance to make UI changes based on states and transitions. Based on that, I think it makes sense for things to happen sequentially so that consumers have some guarantees for what they can expect:

  1. startRecording() is called
  2. [If communication with the service worker succeeds, recordingState becomes 'pending' and the promise returned by startRecording resolves -- this is a minor detail]
  3. The service worker gets ready to record and cache upcoming network requests, then messages back to the client that it's ready
  4. Upon receiving the 'ready' message, these happen synchronously:
    1. recordingState becomes recording
      1. As an example, the Dashboard app uses this cue to turn off progressive loading, so that the whole dashboard is loaded despite some items not being in the viewport
    2. The onStarted callback provided to startRecording() is called
      1. If this is synchronous, recordingState is guaranteed to be 'recording' by this point, so the consumer can more confidently make assumptions about the UI
  5. Similar things happen in response to 'done' and 'error' messages from the service worker

@KaiVandivier KaiVandivier requested a review from kabaros November 22, 2024 15:37
Copy link
Member

@amcgee amcgee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🎉

KaiVandivier and others added 17 commits November 25, 2024 16:39
* fix(deps): remove cli-app-scripts peer dep

* chore: group dependencies in package.json
* **deps:** remove cli-app-scripts peer dep [LIBS-587] ([#1379](#1379)) ([3598375](3598375))
#1380)

* chore(deps): update yarn.lock

* fix(query-playground): update dhis2 deps

* fix: use imported font for consistency across platforms

* fix(query-playground): use codemirror to fix cursor issue

* chore: remove react-ace

* fix: font and tab size

* fix: scrolling for long codemirror blocks

* fix(deps): downgrade @dhis2/cli-app-scripts to work with Node 14

* fix: try lower cli-app-scripts and UI version to fix Node version error
* update plugin sizing definition ([#1383](#1383)) ([266dc49](266dc49))
…1385)

* fix(cacheable-sections): stable references

* refactor: avoid needing eslint disable

* chore: consistent test names
* **cacheable-section:** stable references to avoid loops [LIBS-642] ([#1385](#1385)) ([e3a5fbf](e3a5fbf))
* fixed dimensions efficiency ([#1386](#1386)) ([2b07a14](2b07a14))
* fix: handle async parentAlertsAdd

* chore: fix comment
* handle alert returned async by parentAlertsAdd [LIBS-695] ([#1388](#1388)) ([9c989b2](9c989b2))
* expand FetchErrorDetails type ([#1389](#1389)) ([ff0ad60](ff0ad60))
* add endpoint to text plain matchers ([#1390](#1390)) ([bc25458](bc25458))
@KaiVandivier KaiVandivier force-pushed the fix-type-generation-and-tests branch from f722f7a to 122f06a Compare November 25, 2024 15:51
@KaiVandivier KaiVandivier force-pushed the fix-type-generation-and-tests branch from 122f06a to 04bc604 Compare November 25, 2024 16:08
@amcgee amcgee merged commit 222fa20 into alpha Nov 26, 2024
13 of 15 checks passed
@amcgee amcgee deleted the fix-type-generation-and-tests branch November 26, 2024 15:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants