Skip to content

Commit 16516e5

Browse files
committed
docs: Fix hydration error on mobile
1 parent 91b738f commit 16516e5

File tree

2 files changed

+37
-7
lines changed

2 files changed

+37
-7
lines changed

apps/typegpu-docs/src/components/ExampleLayout.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useAtom, useSetAtom } from 'jotai';
1+
import { useSetAtom } from 'jotai';
22
import type { ReactNode } from 'react';
33
import { useId, useRef } from 'react';
44
import CrossSvg from '../assets/cross.svg';
@@ -13,16 +13,15 @@ import {
1313
import { SearchableExampleList } from './SearchableExampleList.tsx';
1414
import { Button } from './design/Button.tsx';
1515
import { Toggle } from './design/Toggle.tsx';
16-
import { useHydrated } from '../utils/useHydrated.ts';
16+
import { useHydratedAtom } from '../utils/useHydrated.ts';
1717

1818
interface ExampleLayoutProps {
1919
children?: ReactNode | undefined;
2020
}
2121

2222
export function ExampleLayout(props: ExampleLayoutProps) {
23-
const [menuShown, setMenuShown] = useAtom(menuShownAtom);
24-
const [codeShown, setCodeShown] = useAtom(codeEditorShownAtom);
25-
const hydrated = useHydrated();
23+
const [menuShown, setMenuShown] = useHydratedAtom(menuShownAtom, false);
24+
const [codeShown, setCodeShown] = useHydratedAtom(codeEditorShownAtom, false);
2625

2726
return (
2827
<>
@@ -35,7 +34,7 @@ export function ExampleLayout(props: ExampleLayoutProps) {
3534

3635
<Button onClick={() => setCodeShown((prev) => !prev)}>
3736
{/* Applying the actual label only after the component has been hydrated */}
38-
{codeShown && hydrated ? 'Preview' : 'Code'}
37+
{codeShown ? 'Preview' : 'Code'}
3938
</Button>
4039
</div>
4140

@@ -49,8 +48,9 @@ export function ExampleLayout(props: ExampleLayoutProps) {
4948

5049
function SideMenu() {
5150
const setMenuShown = useSetAtom(menuShownAtom);
52-
const [experimentalShowing, setExperimentalShowing] = useAtom(
51+
const [experimentalShowing, setExperimentalShowing] = useHydratedAtom(
5352
experimentalExamplesShownAtom,
53+
true,
5454
);
5555
const scrollRef = useRef<HTMLDivElement>(null);
5656
const experimentalExamplesToggleId = useId();

apps/typegpu-docs/src/utils/useHydrated.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
import {
2+
type ExtractAtomArgs,
3+
type ExtractAtomResult,
4+
type ExtractAtomValue,
5+
useAtom,
6+
type WritableAtom,
7+
} from 'jotai';
18
import { useEffect, useState } from 'react';
29

310
/**
@@ -13,3 +20,26 @@ export function useHydrated() {
1320

1421
return isHydrated;
1522
}
23+
24+
type AnyWritableAtom = WritableAtom<unknown, never[], unknown>;
25+
26+
type UseHydratedAtomReturn<T extends AnyWritableAtom> = [
27+
ExtractAtomValue<T>,
28+
(...args: ExtractAtomArgs<T>) => ExtractAtomResult<T>,
29+
];
30+
31+
/**
32+
* The same as `useAtom`, but returns `unhydratedValue` instead of the atom's value
33+
* if the component is yet to be hydrated.
34+
*/
35+
export function useHydratedAtom<
36+
T extends AnyWritableAtom,
37+
>(atom: T, unhydratedValue: ExtractAtomValue<T>): UseHydratedAtomReturn<T> {
38+
const [atomValue, atomSetter] = useAtom(atom);
39+
const hydrated = useHydrated();
40+
41+
return [
42+
hydrated ? atomValue : unhydratedValue,
43+
atomSetter,
44+
] as UseHydratedAtomReturn<T>;
45+
}

0 commit comments

Comments
 (0)