({children, ...props})
| 51 | } |
| 52 | |
| 53 | export const ThemeProvider: React.FC<React.PropsWithChildren<ThemeProviderProps>> = ({children, ...props}) => { |
| 54 | // Get fallback values from parent ThemeProvider (if exists) |
| 55 | const { |
| 56 | theme: fallbackTheme, |
| 57 | colorMode: fallbackColorMode, |
| 58 | dayScheme: fallbackDayScheme, |
| 59 | nightScheme: fallbackNightScheme, |
| 60 | } = useTheme() |
| 61 | |
| 62 | // Initialize state |
| 63 | const theme = props.theme ?? fallbackTheme ?? defaultTheme |
| 64 | |
| 65 | const uniqueDataId = useId() |
| 66 | const {resolvedServerColorMode} = getServerHandoff(uniqueDataId) |
| 67 | const resolvedColorModePassthrough = React.useRef(resolvedServerColorMode) |
| 68 | |
| 69 | const [colorMode, setColorMode] = React.useState(props.colorMode ?? fallbackColorMode ?? defaultColorMode) |
| 70 | const [dayScheme, setDayScheme] = React.useState(props.dayScheme ?? fallbackDayScheme ?? defaultDayScheme) |
| 71 | const [nightScheme, setNightScheme] = React.useState(props.nightScheme ?? fallbackNightScheme ?? defaultNightScheme) |
| 72 | const systemColorMode = useSystemColorMode() |
| 73 | const resolvedColorMode = resolvedColorModePassthrough.current || resolveColorMode(colorMode, systemColorMode) |
| 74 | const colorScheme = chooseColorScheme(resolvedColorMode, dayScheme, nightScheme) |
| 75 | const {resolvedTheme, resolvedColorScheme} = React.useMemo( |
| 76 | () => applyColorScheme(theme, colorScheme), |
| 77 | [theme, colorScheme], |
| 78 | ) |
| 79 | |
| 80 | // this effect will only run on client |
| 81 | React.useEffect( |
| 82 | function updateColorModeAfterServerPassthrough() { |
| 83 | const resolvedColorModeOnClient = resolveColorMode(colorMode, systemColorMode) |
| 84 | |
| 85 | if (resolvedColorModePassthrough.current) { |
| 86 | // if the resolved color mode passed on from the server is not the resolved color mode on client, change it! |
| 87 | if (resolvedColorModePassthrough.current !== resolvedColorModeOnClient) { |
| 88 | window.setTimeout(() => { |
| 89 | // use ReactDOM.flushSync to prevent automatic batching of state updates since React 18 |
| 90 | // ref: https://github.com/reactwg/react-18/discussions/21 |
| 91 | ReactDOM.flushSync(() => { |
| 92 | // override colorMode to whatever is resolved on the client to get a re-render |
| 93 | setColorMode(resolvedColorModeOnClient) |
| 94 | }) |
| 95 | |
| 96 | // immediately after that, set the colorMode to what the user passed to respond to system color mode changes |
| 97 | setColorMode(colorMode) |
| 98 | }) |
| 99 | } |
| 100 | |
| 101 | resolvedColorModePassthrough.current = null |
| 102 | } |
| 103 | }, |
| 104 | [colorMode, systemColorMode], |
| 105 | ) |
| 106 | |
| 107 | // Update state if props change |
| 108 | React.useEffect(() => { |
| 109 | setColorMode(props.colorMode ?? fallbackColorMode ?? defaultColorMode) |
| 110 | }, [props.colorMode, fallbackColorMode]) |
nothing calls this directly
no test coverage detected