Skip to content

Commit fdad54d

Browse files
authored
Explain limitations of useSyncExternalStore with concurrency (#6339)
* Explain limitations of useSyncExternalStore with concurrency Doesn't seem like we have this noted anywhere. * Pull out example to code block
1 parent 9aa84b1 commit fdad54d

File tree

1 file changed

+21
-1
lines changed

1 file changed

+21
-1
lines changed

src/content/reference/react/useSyncExternalStore.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,26 @@ The current snapshot of the store which you can use in your rendering logic.
5757
5858
* If a different `subscribe` function is passed during a re-render, React will re-subscribe to the store using the newly passed `subscribe` function. You can prevent this by declaring `subscribe` outside the component.
5959
60+
* If the store is mutated during a [non-blocking transition update](/reference/react/useTransition), React will fall back to performing that update as blocking. Specifically, React will call `getSnapshot` a second time just before applying changes to the DOM. If it returns a different value than when it was called originally, React will restart the transition update from scratch, this time applying it as a blocking update, to ensure that every component on screen is reflecting the same version of the store.
61+
62+
* It's not recommended to _suspend_ a render based on a store value returned by `useSyncExternalStore`. The reason is that mutations to the external store cannot be [marked as non-blocking transition updates](/reference/react/useTransition), so they will trigger the nearest [`Suspense` fallback](/reference/react/Suspense), replacing already-rendered content on screen with a loading spinner, which typically makes a poor UX.
63+
64+
For example, the following are discouraged:
65+
66+
```js
67+
const LazyProductDetailPage = lazy(() => import('./ProductDetailPage.js'));
68+
69+
function ShoppingApp() {
70+
const selectedProductId = useSyncExternalStore(...);
71+
72+
// ❌ Calling `use` with a Promise dependent on `selectedProductId`
73+
const data = use(fetchItem(selectedProductId))
74+
75+
// ❌ Conditionally rendering a lazy component based on `selectedProductId`
76+
return selectedProductId != null ? <LazyProductDetailPage /> : <FeaturedProducts />;
77+
}
78+
```
79+
6080
---
6181
6282
## Usage {/*usage*/}
@@ -425,4 +445,4 @@ function ChatIndicator({ userId }) {
425445

426446
// ...
427447
}
428-
```
448+
```

0 commit comments

Comments
 (0)