Search code examples
reactjsreact-hooksreact-dom

Sharing Context between Components in different DOM nodes


Goal: I want to share a Theme context between components that are not wrapped together as children.

// theme_context.jsx

const existingTheme = localStorage.getItem('displayTheme') || 'dark'

export const UserSettings = createContext({
  userTheme: existingTheme,
  setUserTheme: () => { }
});

export const UserSettingsProvider = ({ children }) => {
  const [userTheme, setUserTheme] = useState(existingTheme);
  console.log('getDesignTokens passed', children, userTheme)
  const muiTheme = createTheme(getDesignTokens(userTheme));
  const value = { userTheme, setUserTheme };

  return (
    <UserSettings.Provider value={value}>
      <ThemeProvider theme={muiTheme}>
        <CssBaseline />
        {children}
      </ThemeProvider>
    </UserSettings.Provider>
  )
}

// Problem: provider context theme is not updated for the footer component when trigger is clicked in navbar component, even though they both import the same context.

// navbar.jsx

import { UserSettingsProvider } from '~/components/theme_context'

ReactDOM.render(
    <UserSettingsProvider>
      <ResponsiveAppBar />
    </UserSettingsProvider>,
  document.getElementById('nav-bar')
);

// footer.jsx

import { UserSettingsProvider } from '~/components/theme_context'

ReactDOM.render(
    <UserSettingsProvider>
      <ResponsiveFooter />
    </UserSettingsProvider>,
  document.getElementById('footer')
);

// The following works (both children are updated upon theme change trigger) but it means i can only use one dom node, which i do not want.

import { UserSettingsProvider } from '~/components/theme_context'

ReactDOM.render(
    <UserSettingsProvider>
      <ResponsiveAppBar />
      <ResponsiveFooter />
    </UserSettingsProvider>,
  document.getElementById('root')
);

Is there some way to identify UserSettingsProvider as common for all of its children, without wrapping?


Solution

  • Use Portals, as shown here:

    https://stackoverflow.com/a/50024620/9938721

    Children are wrapped together in the same context but rendered in different nodes.

    Also see: https://stackoverflow.com/a/49426804/9938721