Last active
April 20, 2021 21:06
-
-
Save cwparsons/bcc7f0b0d266356aadfa63f637c7aa80 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Create a SharePoint theme context provider, allowing children components to | |
* use the SharePoint theme and update when the theme changes. | |
* | |
* Usage: | |
* ```typescript | |
* <SpThemeProvider observer={this} serviceScope={this.context.serviceScope}> | |
* {children} | |
* </SpThemeProvider> | |
* ``` | |
* | |
* ```typescript | |
* const theme = useTheme(); | |
* ``` | |
*/ | |
import { IReadonlyTheme, ThemeProvider, ThemeChangedEventArgs } from '@microsoft/sp-component-base'; | |
import { ISPEventObserver, ServiceScope } from '@microsoft/sp-core-library'; | |
import { isEqual } from '@microsoft/sp-lodash-subset'; | |
import { createContext, createElement, useContext, useEffect, useRef, useState } from 'react'; | |
/** | |
* Create the React context for storing the SharePoint theme. | |
*/ | |
export const SpThemeContext = createContext<IReadonlyTheme>(undefined); | |
type SpThemeProviderProps = { | |
children?: React.ReactNode; | |
observer?: ISPEventObserver; | |
serviceScope: ServiceScope; | |
}; | |
/** | |
* Create the provider element that gets the SharePoint theme from the | |
* ThemeProvider, and handles changes to the theme using React state. | |
*/ | |
export const SpThemeProvider = ({ children, observer, serviceScope }: SpThemeProviderProps) => { | |
// Use a ref to lazy load the ThemeProvider service. | |
const themeProviderRef = useRef<ThemeProvider>(null); | |
// Only load the ThemeProvider service once. | |
if (themeProviderRef.current === null) { | |
themeProviderRef.current = serviceScope.consume(ThemeProvider.serviceKey); | |
} | |
// Use a function to only get the theme once. | |
const [theme, setTheme] = useState<IReadonlyTheme>(() => themeProviderRef.current.tryGetTheme()); | |
// Add events to handle changes to the theme. | |
useEffect(() => { | |
if (!observer) { | |
return; | |
} | |
const themeChangeCallback = (themeChangeEventArgs: ThemeChangedEventArgs) => { | |
// Only change the state if the theme is different. | |
if (!isEqual(theme, themeChangeEventArgs.theme)) { | |
setTheme(themeChangeEventArgs.theme); | |
} | |
}; | |
themeProviderRef.current.themeChangedEvent.add(observer, themeChangeCallback); | |
return () => { | |
themeProviderRef.current.themeChangedEvent.remove(observer, themeChangeCallback); | |
}; | |
}, [theme]); | |
return createElement(SpThemeContext.Provider, { | |
children, | |
value: theme | |
}); | |
}; | |
SpThemeProvider.displayName = 'SpThemeProvider'; | |
export const SpThemeConsumer = SpThemeContext.Consumer; | |
export const useTheme = () => { | |
return useContext(SpThemeContext); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment