From 9a97f419d20eca2d35e09aeb89e2ceda18547419 Mon Sep 17 00:00:00 2001 From: Aurora Scharff Date: Thu, 20 Aug 2026 23:51:59 +0200 Subject: [PATCH 1/8] Add conditional browser query example --- src/content/reference/react-dom/browser.md | 176 +++++++++++++++++++++ 1 file changed, 176 insertions(+) diff --git a/src/content/reference/react-dom/browser.md b/src/content/reference/react-dom/browser.md index 017da34e7d2..495f2afeaa7 100644 --- a/src/content/reference/react-dom/browser.md +++ b/src/content/reference/react-dom/browser.md @@ -258,6 +258,182 @@ function ProductDetails({ productId, initialData }) { On the server, `useBrowserQuery` calls `useQuery` only when `initialData` is available. Otherwise, the closest Suspense boundary's fallback remains in the HTML. In the browser, `use(browser())` returns `undefined`, so the query library can fetch the data or read it from its client cache. +This example renders one query with initial data and one without it. + +Click **Reload** to see the second product's loading fallback before its query resolves. + + + +```js src/App.js active +import { Suspense, use } from 'react'; +import { browser } from 'react-dom'; +import { useQuery } from './query.js'; + +function useBrowserQuery(query, options) { + if (options.initialData === undefined) { + use(browser('useBrowserQuery: No initial data was provided.')); + } + return useQuery(query, options); +} + +function ProductDetails({productId, initialData}) { + const product = useBrowserQuery(`/api/products/${productId}`, { + initialData, + }); + return {product.name}; +} + +export default function App() { + return ( + <> +

Featured products

+ + + ); +} +``` + +```js src/query.js hidden +import { use } from 'react'; + +// This is a simplified implementation of a +// Suspense-enabled query library. + +const products = { + '/api/products/react-shirt': {name: 'React shirt'}, +}; + +const cache = new Map(); + +function fetchProduct(query) { + if (!cache.has(query)) { + cache.set( + query, + new Promise(resolve => { + setTimeout(() => resolve(products[query]), 600); + }) + ); + } + return cache.get(query); +} + +export function useQuery(query, options) { + if (options.initialData !== undefined) { + return options.initialData; + } + return use(fetchProduct(query)); +} +``` + +```js src/Document.js hidden +import App from './App.js'; + +export default function Document() { + return ( + + + Featured products + + + + + + + ); +} +``` + +```js src/index.js hidden +import { hydrateRoot } from 'react-dom/client'; +import { renderToReadableStream } from 'react-dom/server'; +import Document from './Document.js'; +import { flushReadableStreamToFrame } from './demo-helpers.js'; +import './styles.css'; + +async function main(frame) { + const stream = await renderToReadableStream(); + await flushReadableStreamToFrame(stream, frame); + + // Wait so both the fallback and hydrated content are visible. + await new Promise(resolve => setTimeout(resolve, 1200)); + hydrateRoot(frame.contentDocument, ); +} + +main(document.getElementById('preview')); +``` + +```js src/demo-helpers.js hidden +export async function flushReadableStreamToFrame(readable, frame) { + const doc = frame.contentWindow.document; + const decoder = new TextDecoder(); + const reader = readable.getReader(); + + while (true) { + const {done, value} = await reader.read(); + if (done) { + break; + } + doc.write(decoder.decode(value, {stream: true})); + } + + doc.write(decoder.decode()); + doc.close(); +} +``` + +```html public/index.html hidden + + + + + Conditional browser rendering + + + + + +``` + +```css src/styles.css hidden +iframe { + width: 100%; + height: 170px; + border: 0; +} +``` + +```json package.json hidden +{ + "dependencies": { + "react": "19.3.0-canary-eb8feb71-20260814", + "react-dom": "19.3.0-canary-eb8feb71-20260814", + "react-scripts": "latest" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test --env=jsdom", + "eject": "react-scripts eject" + } +} +``` + +
+ --- ### Reporting browser-only rendering on the server {/*reporting-browser-only-rendering-on-the-server*/} From 9247c01776db1e48aee9876685d202c75ca7a952 Mon Sep 17 00:00:00 2001 From: Aurora Scharff Date: Mon, 24 Aug 2026 13:12:25 +0200 Subject: [PATCH 2/8] Move browser query hook into its own example file --- src/content/reference/react-dom/browser.md | 25 +++++++++++++--------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/content/reference/react-dom/browser.md b/src/content/reference/react-dom/browser.md index 495f2afeaa7..a08fe807ba6 100644 --- a/src/content/reference/react-dom/browser.md +++ b/src/content/reference/react-dom/browser.md @@ -265,16 +265,8 @@ Click **Reload** to see the second product's loading fallback before its query r ```js src/App.js active -import { Suspense, use } from 'react'; -import { browser } from 'react-dom'; -import { useQuery } from './query.js'; - -function useBrowserQuery(query, options) { - if (options.initialData === undefined) { - use(browser('useBrowserQuery: No initial data was provided.')); - } - return useQuery(query, options); -} +import { Suspense } from 'react'; +import { useBrowserQuery } from './useBrowserQuery.js'; function ProductDetails({productId, initialData}) { const product = useBrowserQuery(`/api/products/${productId}`, { @@ -305,6 +297,19 @@ export default function App() { } ``` +```js src/useBrowserQuery.js +import { use } from 'react'; +import { browser } from 'react-dom'; +import { useQuery } from './query.js'; + +export function useBrowserQuery(query, options) { + if (options.initialData === undefined) { + use(browser('useBrowserQuery: No initial data was provided.')); + } + return useQuery(query, options); +} +``` + ```js src/query.js hidden import { use } from 'react'; From 262b84a663fac9000d83cb5e47c5841ba0108e9e Mon Sep 17 00:00:00 2001 From: Aurora Scharff Date: Mon, 24 Aug 2026 19:41:18 +0200 Subject: [PATCH 3/8] Use IndexedDB in conditional browser example --- src/content/reference/react-dom/browser.md | 125 +++++++++++---------- 1 file changed, 63 insertions(+), 62 deletions(-) diff --git a/src/content/reference/react-dom/browser.md b/src/content/reference/react-dom/browser.md index a08fe807ba6..61faee3fd3f 100644 --- a/src/content/reference/react-dom/browser.md +++ b/src/content/reference/react-dom/browser.md @@ -236,60 +236,53 @@ export default function SavedDraft() { ### Conditionally rendering in the browser {/*conditionally-rendering-in-the-browser*/} -Like other calls to [`use`](/reference/react/use), you can call `use(browser())` conditionally or inside a custom Hook. For example, you can wrap a Suspense-enabled data-fetching library's `useQuery` and skip server rendering when initial data is missing: +Like other calls to [`use`](/reference/react/use), you can call `use(browser())` conditionally or inside a custom Hook. For example, a custom Hook can use initial data when it is available during server rendering, and read from IndexedDB in the browser when it isn't: ```js {3} -function useBrowserQuery(query, options) { - if (options.initialData === undefined) { - use(browser('useBrowserQuery: No initial data was provided.')); +function useDraft(draftId, initialDraft) { + if (initialDraft !== undefined) { + return initialDraft; } - return useQuery(query, options); -} - -function ProductDetails({ productId, initialData }) { - const product = useBrowserQuery(`/api/products/${productId}`, { - initialData, - }); - - return

{product.name}

; + use(browser('The draft is stored in IndexedDB.')); + return use(readDraft(draftId)); } ``` -On the server, `useBrowserQuery` calls `useQuery` only when `initialData` is available. Otherwise, the closest Suspense boundary's fallback remains in the HTML. In the browser, `use(browser())` returns `undefined`, so the query library can fetch the data or read it from its client cache. +On the server, `useDraft` returns `initialDraft` when it is provided. Otherwise, the closest Suspense boundary's fallback remains in the HTML. In the browser, `use(browser())` returns `undefined`, so the Hook continues and reads the draft from IndexedDB. -This example renders one query with initial data and one without it. +This example renders one draft with initial data and one stored in IndexedDB. -Click **Reload** to see the second product's loading fallback before its query resolves. +Click **Reload** to see the second draft's loading fallback while React reads it from IndexedDB. ```js src/App.js active import { Suspense } from 'react'; -import { useBrowserQuery } from './useBrowserQuery.js'; +import { useDraft } from './useDraft.js'; -function ProductDetails({productId, initialData}) { - const product = useBrowserQuery(`/api/products/${productId}`, { - initialData, - }); - return {product.name}; +function Draft({draftId, title, initialDraft}) { + const draft = useDraft(draftId, initialDraft); + return ( +
  • + {title} +

    {draft}

    +
  • + ); } export default function App() { return ( <> -

    Featured products

    +

    Saved drafts

      -
    • - -
    • - Loading another product...}> -
    • - -
    • + + Loading saved draft...}> +
    @@ -297,48 +290,56 @@ export default function App() { } ``` -```js src/useBrowserQuery.js +```js src/useDraft.js import { use } from 'react'; import { browser } from 'react-dom'; -import { useQuery } from './query.js'; +import { readDraft } from './database.js'; -export function useBrowserQuery(query, options) { - if (options.initialData === undefined) { - use(browser('useBrowserQuery: No initial data was provided.')); +export function useDraft(draftId, initialDraft) { + if (initialDraft !== undefined) { + return initialDraft; } - return useQuery(query, options); + + use(browser('The draft is stored in IndexedDB.')); + return use(readDraft(draftId)); } ``` -```js src/query.js hidden -import { use } from 'react'; - -// This is a simplified implementation of a -// Suspense-enabled query library. - -const products = { - '/api/products/react-shirt': {name: 'React shirt'}, +```js src/database.js hidden +const drafts = { + 'trip-notes': 'Remember to pack a charger.', }; const cache = new Map(); -function fetchProduct(query) { - if (!cache.has(query)) { - cache.set( - query, - new Promise(resolve => { - setTimeout(() => resolve(products[query]), 600); - }) - ); +export function readDraft(draftId) { + if (!cache.has(draftId)) { + cache.set(draftId, readDraftFromIndexedDB(draftId)); } - return cache.get(query); + return cache.get(draftId); } -export function useQuery(query, options) { - if (options.initialData !== undefined) { - return options.initialData; - } - return use(fetchProduct(query)); +function readDraftFromIndexedDB(draftId) { + return new Promise((resolve, reject) => { + const request = indexedDB.open('browser-example', 1); + + request.onupgradeneeded = () => { + const store = request.result.createObjectStore('drafts'); + for (const [key, value] of Object.entries(drafts)) { + store.add(value, key); + } + }; + + request.onerror = () => reject(request.error); + request.onsuccess = () => { + const transaction = request.result.transaction('drafts'); + const draftRequest = transaction.objectStore('drafts').get(draftId); + draftRequest.onerror = () => reject(draftRequest.error); + draftRequest.onsuccess = () => { + setTimeout(() => resolve(draftRequest.result), 600); + }; + }; + }); } ``` @@ -349,7 +350,7 @@ export default function Document() { return ( - Featured products + Saved drafts From 4bb86b6e8b4f4a944f55a538f12055c142d0fddf Mon Sep 17 00:00:00 2001 From: Aurora Scharff Date: Mon, 24 Aug 2026 19:49:54 +0200 Subject: [PATCH 4/8] Clarify conditional browser rendering example --- src/content/reference/react-dom/browser.md | 96 ++++++++++------------ 1 file changed, 45 insertions(+), 51 deletions(-) diff --git a/src/content/reference/react-dom/browser.md b/src/content/reference/react-dom/browser.md index 61faee3fd3f..9869bbd3fdb 100644 --- a/src/content/reference/react-dom/browser.md +++ b/src/content/reference/react-dom/browser.md @@ -236,37 +236,34 @@ export default function SavedDraft() { ### Conditionally rendering in the browser {/*conditionally-rendering-in-the-browser*/} -Like other calls to [`use`](/reference/react/use), you can call `use(browser())` conditionally or inside a custom Hook. For example, a custom Hook can use initial data when it is available during server rendering, and read from IndexedDB in the browser when it isn't: +Like other calls to [`use`](/reference/react/use), you can call `use(browser())` conditionally or inside a custom Hook. For example, a custom Hook can return an initial value when it is provided, and read it from IndexedDB in the browser when it isn't: -```js {3} -function useDraft(draftId, initialDraft) { - if (initialDraft !== undefined) { - return initialDraft; +```js {6} +function useSetting(settingId, initialValue) { + if (initialValue !== undefined) { + return initialValue; } - use(browser('The draft is stored in IndexedDB.')); - return use(readDraft(draftId)); + use(browser('No initial setting was provided.')); + return use(readSetting(settingId)); } ``` -On the server, `useDraft` returns `initialDraft` when it is provided. Otherwise, the closest Suspense boundary's fallback remains in the HTML. In the browser, `use(browser())` returns `undefined`, so the Hook continues and reads the draft from IndexedDB. +On the server, `useSetting` returns `initialValue` when it is provided. Otherwise, the closest Suspense boundary's fallback remains in the HTML. In the browser, `use(browser())` returns `undefined`, so the Hook continues and reads the setting from IndexedDB. -This example renders one draft with initial data and one stored in IndexedDB. - -Click **Reload** to see the second draft's loading fallback while React reads it from IndexedDB. +In this example, the email notification setting is provided as initial data. The push notification setting is not, so click **Reload** to see its loading fallback while React reads it from IndexedDB. ```js src/App.js active import { Suspense } from 'react'; -import { useDraft } from './useDraft.js'; +import { useSetting } from './useSetting.js'; -function Draft({draftId, title, initialDraft}) { - const draft = useDraft(draftId, initialDraft); +function NotificationSetting({settingId, label, initialValue}) { + const enabled = useSetting(settingId, initialValue); return (
  • - {title} -

    {draft}

    + {label}: {enabled ? 'On' : 'Off'}
  • ); } @@ -274,15 +271,18 @@ function Draft({draftId, title, initialDraft}) { export default function App() { return ( <> -

    Saved drafts

    +

    Notification settings

      - - Loading saved draft...}> - + Loading push notification setting...}> +
    @@ -290,53 +290,47 @@ export default function App() { } ``` -```js src/useDraft.js +```js src/useSetting.js import { use } from 'react'; import { browser } from 'react-dom'; -import { readDraft } from './database.js'; +import { readSetting } from './database.js'; -export function useDraft(draftId, initialDraft) { - if (initialDraft !== undefined) { - return initialDraft; +export function useSetting(settingId, initialValue) { + if (initialValue !== undefined) { + return initialValue; } - use(browser('The draft is stored in IndexedDB.')); - return use(readDraft(draftId)); + use(browser('No initial setting was provided.')); + return use(readSetting(settingId)); } ``` ```js src/database.js hidden -const drafts = { - 'trip-notes': 'Remember to pack a charger.', -}; - const cache = new Map(); -export function readDraft(draftId) { - if (!cache.has(draftId)) { - cache.set(draftId, readDraftFromIndexedDB(draftId)); +export function readSetting(settingId) { + if (!cache.has(settingId)) { + cache.set(settingId, readSettingFromIndexedDB(settingId)); } - return cache.get(draftId); + return cache.get(settingId); } -function readDraftFromIndexedDB(draftId) { +function readSettingFromIndexedDB(settingId) { return new Promise((resolve, reject) => { - const request = indexedDB.open('browser-example', 1); + const request = indexedDB.open('browser-notification-settings-example', 1); request.onupgradeneeded = () => { - const store = request.result.createObjectStore('drafts'); - for (const [key, value] of Object.entries(drafts)) { - store.add(value, key); - } + const store = request.result.createObjectStore('settings'); + store.add(true, 'push'); }; request.onerror = () => reject(request.error); request.onsuccess = () => { - const transaction = request.result.transaction('drafts'); - const draftRequest = transaction.objectStore('drafts').get(draftId); - draftRequest.onerror = () => reject(draftRequest.error); - draftRequest.onsuccess = () => { - setTimeout(() => resolve(draftRequest.result), 600); + const transaction = request.result.transaction('settings'); + const settingRequest = transaction.objectStore('settings').get(settingId); + settingRequest.onerror = () => reject(settingRequest.error); + settingRequest.onsuccess = () => { + setTimeout(() => resolve(settingRequest.result), 600); }; }; }); @@ -350,7 +344,7 @@ export default function Document() { return ( - Saved drafts + Notification settings @@ -417,7 +411,7 @@ export async function flushReadableStreamToFrame(readable, frame) { ```css src/styles.css hidden iframe { width: 100%; - height: 170px; + height: 240px; border: 0; } ``` From fd80b33b9081dadc6bc9a77c65cd1fde1698280d Mon Sep 17 00:00:00 2001 From: Aurora Scharff Date: Mon, 24 Aug 2026 19:57:02 +0200 Subject: [PATCH 5/8] Polish browser rendering examples --- src/content/reference/react-dom/browser.md | 31 ++++++++++++++-------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/content/reference/react-dom/browser.md b/src/content/reference/react-dom/browser.md index 9869bbd3fdb..6683d8e2002 100644 --- a/src/content/reference/react-dom/browser.md +++ b/src/content/reference/react-dom/browser.md @@ -236,26 +236,33 @@ export default function SavedDraft() { ### Conditionally rendering in the browser {/*conditionally-rendering-in-the-browser*/} -Like other calls to [`use`](/reference/react/use), you can call `use(browser())` conditionally or inside a custom Hook. For example, a custom Hook can return an initial value when it is provided, and read it from IndexedDB in the browser when it isn't: +Like other calls to [`use`](/reference/react/use), you can call `use(browser())` conditionally or inside a custom Hook. For example, you can wrap a Suspense-enabled data-fetching library's `useQuery` and skip server rendering when initial data is missing: -```js {6} -function useSetting(settingId, initialValue) { - if (initialValue !== undefined) { - return initialValue; +```js {3} +function useBrowserQuery(query, options) { + if (options.initialData === undefined) { + use(browser('useBrowserQuery: No initial data was provided.')); } - use(browser('No initial setting was provided.')); - return use(readSetting(settingId)); + return useQuery(query, options); +} + +function ProductDetails({ productId, initialData }) { + const product = useBrowserQuery(`/api/products/${productId}`, { + initialData, + }); + + return

    {product.name}

    ; } ``` -On the server, `useSetting` returns `initialValue` when it is provided. Otherwise, the closest Suspense boundary's fallback remains in the HTML. In the browser, `use(browser())` returns `undefined`, so the Hook continues and reads the setting from IndexedDB. +On the server, `useBrowserQuery` calls `useQuery` only when `initialData` is available. Otherwise, the closest Suspense boundary's fallback remains in the HTML. In the browser, `use(browser())` returns `undefined`, so the query library can fetch the data or read it from its client cache. -In this example, the email notification setting is provided as initial data. The push notification setting is not, so click **Reload** to see its loading fallback while React reads it from IndexedDB. +Here is a complete example using notification settings stored in IndexedDB. The email notification setting receives initial data, but the push notification setting does not. Click **Reload** to see the email setting in the initial HTML while the push setting shows a loading fallback. -```js src/App.js active +```js src/App.js import { Suspense } from 'react'; import { useSetting } from './useSetting.js'; @@ -290,7 +297,7 @@ export default function App() { } ``` -```js src/useSetting.js +```js src/useSetting.js active import { use } from 'react'; import { browser } from 'react-dom'; import { readSetting } from './database.js'; @@ -306,6 +313,8 @@ export function useSetting(settingId, initialValue) { ``` ```js src/database.js hidden +// This is a simplified IndexedDB wrapper for this example. + const cache = new Map(); export function readSetting(settingId) { From 650028b8846209a7fbe5f6f0b9a637d5b6b77534 Mon Sep 17 00:00:00 2001 From: Aurora Scharff Date: Mon, 24 Aug 2026 21:59:09 +0200 Subject: [PATCH 6/8] Bridge conditional browser examples --- src/content/reference/react-dom/browser.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/reference/react-dom/browser.md b/src/content/reference/react-dom/browser.md index 6683d8e2002..5234b4034dd 100644 --- a/src/content/reference/react-dom/browser.md +++ b/src/content/reference/react-dom/browser.md @@ -258,7 +258,7 @@ function ProductDetails({ productId, initialData }) { On the server, `useBrowserQuery` calls `useQuery` only when `initialData` is available. Otherwise, the closest Suspense boundary's fallback remains in the HTML. In the browser, `use(browser())` returns `undefined`, so the query library can fetch the data or read it from its client cache. -Here is a complete example using notification settings stored in IndexedDB. The email notification setting receives initial data, but the push notification setting does not. Click **Reload** to see the email setting in the initial HTML while the push setting shows a loading fallback. +Another way to use conditional `use(browser())` is to read from a browser-only data source when initial data is unavailable. In this example, `useSetting` receives an initial value for the email notification setting, so React can include it in the initial HTML. The push notification setting has no initial value, so `useSetting` calls `use(browser())` before reading it from IndexedDB. Click **Reload** to see both paths: the email setting appears immediately, while the push setting shows a loading fallback until `useSetting` reads it from IndexedDB in the browser. From 44b7dd58ca2c40dceb6eed54ba8a145a3efb36a0 Mon Sep 17 00:00:00 2001 From: Aurora Scharff Date: Mon, 24 Aug 2026 22:01:13 +0200 Subject: [PATCH 7/8] Tighten conditional browser example intro --- src/content/reference/react-dom/browser.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/reference/react-dom/browser.md b/src/content/reference/react-dom/browser.md index 5234b4034dd..ef7904b6ae1 100644 --- a/src/content/reference/react-dom/browser.md +++ b/src/content/reference/react-dom/browser.md @@ -258,7 +258,7 @@ function ProductDetails({ productId, initialData }) { On the server, `useBrowserQuery` calls `useQuery` only when `initialData` is available. Otherwise, the closest Suspense boundary's fallback remains in the HTML. In the browser, `use(browser())` returns `undefined`, so the query library can fetch the data or read it from its client cache. -Another way to use conditional `use(browser())` is to read from a browser-only data source when initial data is unavailable. In this example, `useSetting` receives an initial value for the email notification setting, so React can include it in the initial HTML. The push notification setting has no initial value, so `useSetting` calls `use(browser())` before reading it from IndexedDB. Click **Reload** to see both paths: the email setting appears immediately, while the push setting shows a loading fallback until `useSetting` reads it from IndexedDB in the browser. +You can also use this pattern to read from a browser-only data source when initial data isn't available. In this example, the email setting has initial data, but the push setting is read from IndexedDB. Click **Reload** to see the loading fallback for the push setting. From fa4a59c60a73c18ba8a8e8fbba40481850c1d696 Mon Sep 17 00:00:00 2001 From: Aurora Scharff Date: Mon, 24 Aug 2026 22:07:14 +0200 Subject: [PATCH 8/8] Use time zone for conditional browser example --- src/content/reference/react-dom/browser.md | 85 +++++----------------- 1 file changed, 20 insertions(+), 65 deletions(-) diff --git a/src/content/reference/react-dom/browser.md b/src/content/reference/react-dom/browser.md index ef7904b6ae1..b64bbc4ef05 100644 --- a/src/content/reference/react-dom/browser.md +++ b/src/content/reference/react-dom/browser.md @@ -258,91 +258,46 @@ function ProductDetails({ productId, initialData }) { On the server, `useBrowserQuery` calls `useQuery` only when `initialData` is available. Otherwise, the closest Suspense boundary's fallback remains in the HTML. In the browser, `use(browser())` returns `undefined`, so the query library can fetch the data or read it from its client cache. -You can also use this pattern to read from a browser-only data source when initial data isn't available. In this example, the email setting has initial data, but the push setting is read from IndexedDB. Click **Reload** to see the loading fallback for the push setting. +You can also call `use(browser())` only when initial data isn't available. In this example, the event time zone is provided as initial data, but the user's time zone is read from the browser. Click **Reload** to see the loading fallback for the user's time zone. ```js src/App.js import { Suspense } from 'react'; -import { useSetting } from './useSetting.js'; +import { useTimeZone } from './useTimeZone.js'; -function NotificationSetting({settingId, label, initialValue}) { - const enabled = useSetting(settingId, initialValue); - return ( -
  • - {label}: {enabled ? 'On' : 'Off'} -
  • - ); +function TimeZone({label, initialTimeZone}) { + const timeZone = useTimeZone(initialTimeZone); + return

    {label}: {timeZone}

    ; } export default function App() { return ( <> -

    Notification settings

    -
      - - Loading push notification setting...}> - - -
    +

    Event details

    + + Loading your time zone...

    }> + +
    ); } ``` -```js src/useSetting.js active +```js src/useTimeZone.js active import { use } from 'react'; import { browser } from 'react-dom'; -import { readSetting } from './database.js'; -export function useSetting(settingId, initialValue) { - if (initialValue !== undefined) { - return initialValue; +export function useTimeZone(initialTimeZone) { + if (initialTimeZone !== undefined) { + return initialTimeZone; } - use(browser('No initial setting was provided.')); - return use(readSetting(settingId)); -} -``` - -```js src/database.js hidden -// This is a simplified IndexedDB wrapper for this example. - -const cache = new Map(); - -export function readSetting(settingId) { - if (!cache.has(settingId)) { - cache.set(settingId, readSettingFromIndexedDB(settingId)); - } - return cache.get(settingId); -} - -function readSettingFromIndexedDB(settingId) { - return new Promise((resolve, reject) => { - const request = indexedDB.open('browser-notification-settings-example', 1); - - request.onupgradeneeded = () => { - const store = request.result.createObjectStore('settings'); - store.add(true, 'push'); - }; - - request.onerror = () => reject(request.error); - request.onsuccess = () => { - const transaction = request.result.transaction('settings'); - const settingRequest = transaction.objectStore('settings').get(settingId); - settingRequest.onerror = () => reject(settingRequest.error); - settingRequest.onsuccess = () => { - setTimeout(() => resolve(settingRequest.result), 600); - }; - }; - }); + use(browser('No initial time zone was provided.')); + return Intl.DateTimeFormat().resolvedOptions().timeZone; } ``` @@ -353,7 +308,7 @@ export default function Document() { return ( - Notification settings + Event details