Skip to content

Commit

Permalink
fix: improve color scheme store perf
Browse files Browse the repository at this point in the history
  • Loading branch information
stipsan committed Dec 16, 2024
1 parent 4a6eb2a commit 558cd7a
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 40 deletions.
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 558cd7a

Please sign in to comment.