How to get ThemeContext state as variable in Javascript inside react component?

Viewed 237

I'm using Gatsby to make a simple ReactJS app that uses useContext hook and ThemeContext to add an option of dark theme to the app. Now, I have another component that uses react-helmet to add external JS files to the page. There is some code in these files that I want to connect to the theme state.

I am able to access the theme state on the 'index.js' page as :

const { dark } = useContext(ThemeContext)

I want to write some code in the external file 'myscript.js' like

let isDark = dark
if(isDark){
   console.log("Dark theme")
}
else{
   console.log("Light theme")
}

where 'dark' is from the theme.

1 Answers

You could set a global data property on the window object that you can use anywhere in your project. You can name this anything on the window. In this case I've use __globalData__ to prevent name collision.

window.__globalData__ = {
  theme: 'light'
}

From within the Provider component of the context use useEffect to listen for changes in the theme and update the global value if necessary.

import React, { useState, useEffect } from 'react'
import ThemeContext from './ThemeContext' // Assuming this is the path

const ThemeProvider = ({ children }) => {
  const [ theme, setTheme ] = useState('light')
  
  useEffect(() => {
    window.__globalData__ = window.__globalData__ || {} // Make sure global data is defined.
    window.__globalData__.theme = theme
  }, [theme])

  return(
    <ThemeContext.Provider values={[ theme, setTheme ]}>
      {children}
    </ThemeContext.Provider>
  )
}

export default ThemeProvider

Then use a function to check the value of the global property.

/**
 * Check if dark theme is set when globalData is set on window object.
 * Or return false when globalData is not set, meaning theme is light.
 */
const isDark = () => 
  window.__globalData__ && window.__globalData__.theme ? 
    window.__globalData__.theme === 'dark' :
    false

if (isDark()) {
  console.log("Dark theme")
}
else{
  console.log("Light theme")
}

Alternatively you could use events to do something whenever the theme changes.

To stay in the world of hooks, you could create your own hook that dispatches a CustomEvent with the current theme as detail property.

const useThemeEvents = () => {
  const dispatch = theme => {
    const event = new CustomEvent('themechange', {
      detail: { theme }
    })
    window.dispatchEvent(event)
  }
  return { dispatch }
}

export { useThemeEvents }

And then use the hook in your provider component.

import React, { useState, useEffect } from 'react'
import ThemeContext from './ThemeContext' // Assuming this is the path
import { useThemeEvents } from './hooks' // Also assuming this is the path

const ThemeProvider = ({ children }) => {
  const [ theme, setTheme ] = useState('light')
  const { dispatch } = useThemeEvents()
  
  useEffect(() => {
    dispatch(theme)
  }, [theme])

  return(
    <ThemeContext.Provider values={[ theme, setTheme ]}>
      {children}
    </ThemeContext.Provider>
  )
}

export default ThemeProvider

Now in your added JS file listen for the themechange event on the window object.

window.addEventListener('themechange', ({ detail }) => {
  const { theme } = detail
  if (theme === 'dark') {
    console.log("Dark theme")
  } else {
    console.log("Light theme")
  }
})
Related