Skip to content

Commit

Permalink
fix: improve color scheme store perf (#8059)
Browse files Browse the repository at this point in the history
  • Loading branch information
stipsan authored Dec 16, 2024
1 parent 80965bf commit ed3551b
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 41 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"check:deps": "pnpm --recursive --parallel exec depcheck",
"check:format": "prettier . --check",
"check:lint": "turbo run lint --continue -- --quiet",
"check:react-compiler": "eslint --cache --no-inline-config --no-eslintrc --ext .cjs,.mjs,.js,.jsx,.ts,.tsx --parser @typescript-eslint/parser --plugin react-compiler --rule 'react-compiler/react-compiler: [warn]' --ignore-path .eslintignore.react-compiler --max-warnings 77 .",
"check:react-compiler": "eslint --cache --no-inline-config --no-eslintrc --ext .cjs,.mjs,.js,.jsx,.ts,.tsx --parser @typescript-eslint/parser --plugin react-compiler --rule 'react-compiler/react-compiler: [warn]' --ignore-path .eslintignore.react-compiler --max-warnings 76 .",
"report:react-compiler-bailout": "eslint --cache --no-inline-config --no-eslintrc --ext .cjs,.mjs,.js,.jsx,.ts,.tsx --parser @typescript-eslint/parser --plugin react-compiler --rule 'react-compiler/react-compiler: [error,{__unstable_donotuse_reportAllBailouts:true}]' --ignore-path .eslintignore.react-compiler -f ./scripts/reactCompilerBailouts.cjs . || true",
"check:test": "run-s test -- --silent",
"check:types": "tsc && turbo run check:types --filter='./packages/*' --filter='./packages/@sanity/*'",
Expand Down
51 changes: 11 additions & 40 deletions packages/sanity/src/core/studio/colorScheme.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {ColorSchemeSetValueContext, ColorSchemeValueContext} from 'sanity/_singl

import {type TFunction} from '../i18n'
import {type StudioThemeColorSchemeKey} from '../theme/types'
import {getSnapshot, setSnapshot, subscribe} from './colorSchemeStore'

/** @internal */
function useSystemScheme(): ThemeColorSchemeKey {
Expand Down Expand Up @@ -78,33 +79,13 @@ export function ColorSchemeLocalStorageProvider({
children,
onSchemeChange,
}: Pick<ColorSchemeProviderProps, 'children' | 'onSchemeChange'>) {
const store = useMemo(() => {
let snapshot: StudioThemeColorSchemeKey
const subscribers = new Set<() => void>()

return {
subscribe: (onStoreChange: () => void) => {
if (!snapshot) {
snapshot = getScheme(localStorage.getItem(LOCAL_STORAGE_KEY)) || 'system'
}
subscribers.add(onStoreChange)
return () => {
subscribers.delete(onStoreChange)
}
},
getSnapshot: () => snapshot,
setSnapshot: (nextScheme: StudioThemeColorSchemeKey) => {
snapshot = getScheme(nextScheme)
for (const subscription of subscribers) {
subscription()
}
},
// Only called during server-side rendering, and hydration if using hydrateRoot
// https://beta.reactjs.org/apis/react/useSyncExternalStore#adding-support-for-server-rendering
getServerSnapshot: () => 'system',
}
}, [])
const scheme = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getServerSnapshot)
const scheme = useSyncExternalStore<StudioThemeColorSchemeKey>(
subscribe,
getSnapshot,
// Only called during server-side rendering, and hydration if using hydrateRoot
// https://beta.reactjs.org/apis/react/useSyncExternalStore#adding-support-for-server-rendering
() => 'system',
)

useEffect(() => {
if (typeof onSchemeChange === 'function') {
Expand All @@ -114,24 +95,14 @@ export function ColorSchemeLocalStorageProvider({
}, [onSchemeChange, scheme])

return (
<ColorSchemeSetValueContext.Provider value={store.setSnapshot}>
<ColorSchemeSetValueContext.Provider value={setSnapshot}>
<ColorSchemeValueContext.Provider value={scheme}>
<ColorThemeProvider scheme={scheme}>{children}</ColorThemeProvider>
</ColorSchemeValueContext.Provider>
</ColorSchemeSetValueContext.Provider>
)
}

function getScheme(scheme: unknown): StudioThemeColorSchemeKey {
switch (scheme) {
case 'dark':
case 'light':
return scheme
default:
return 'system'
}
}

/**
* If the `scheme` prop is provided we don't need to setup any logic to handle localStorage
* @internal
Expand Down Expand Up @@ -210,7 +181,7 @@ export function useColorSchemeOptions(
) {
const scheme = useColorSchemeInternalValue()

return useMemo(() => {
return useMemo<ColorSchemeOption[]>(() => {
return [
{
title: t('user-menu.color-scheme.system-title'),
Expand All @@ -236,6 +207,6 @@ export function useColorSchemeOptions(
onSelect: () => setScheme('light'),
icon: SunIcon,
},
] satisfies ColorSchemeOption[]
]
}, [scheme, setScheme, t])
}
39 changes: 39 additions & 0 deletions packages/sanity/src/core/studio/colorSchemeStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {type StudioThemeColorSchemeKey} from '../theme/types'

function getScheme(scheme: unknown): StudioThemeColorSchemeKey {
switch (scheme) {
case 'dark':
case 'light':
return scheme
default:
return 'system'
}
}

/** @internal */
export const LOCAL_STORAGE_KEY = 'sanityStudio:ui:colorScheme'

let snapshot: StudioThemeColorSchemeKey
const subscribers = new Set<() => void>()

/** @internal */
export const subscribe = (onStoreChange: () => void) => {
if (!snapshot) {
snapshot = getScheme(localStorage.getItem(LOCAL_STORAGE_KEY)) || 'system'
}
subscribers.add(onStoreChange)
return (): void => {
subscribers.delete(onStoreChange)
}
}
/** @internal */
export function getSnapshot(): StudioThemeColorSchemeKey {
return snapshot
}
/** @internal */
export function setSnapshot(nextScheme: StudioThemeColorSchemeKey): void {
snapshot = getScheme(nextScheme)
for (const subscription of subscribers) {
subscription()
}
}

0 comments on commit ed3551b

Please sign in to comment.