2

I want to include Theme UI's presets in my GatsbyJS for switching the mode (dark and light) and the themes.

I can't modify export of my original code to merge two const functions into an export, because export needs children to be defined. I will not want to use children, because another layout file in JSX has already children. I also can't merge a const into another const. You will check the errors at the end. I'll share my repo's small folder at the end for you to test. The In Angular2+ is there any way to extends on inherit const into another const? does not help either.

See my small original code, and notice const and export:

import React from 'react'
import {Context} from '../../common'
import SelectLanguage from './SelectLanguage'
import {Links} from './styles'
import {Navegador, StyledHeader} from './styles'

const Header = () => (
  <Context.Consumer>
    {({ toggleLanguage, lang }) => (
      <StyledHeader>
        <Navegador id="menu">
          <li>
            <Links>
              {/* ... */}
              <SelectLanguage lang={lang} toggleLanguage={toggleLanguage} />
            </Links>
          </li>
        </Navegador>
      </StyledHeader>
    )}
  </Context.Consumer>
)

export default Header

My another small Theme UI code, you can notice const functions inside the export:

/** @jsx jsx */

import { jsx, Styled, useColorMode } from 'theme-ui'
import ButtonUI from '../../theme-ui/button-ui'

const themes = ['deep', 'funk', 'future', 'swiss']
const modes = ['default', 'dark']

const getThemeName = (theme) => 
{
  switch (theme)
  {
    case 'deep':
      return 'Deep'
    case 'funk':
      return 'Funk'
    case 'future':
      return 'Future'
    case 'swiss':
      return 'Swiss'
    default:
        return theme
  }
}

const getModeName = (mode) => 
{
  switch (mode)
  {
    case 'dark':
      return (<span role="img" aria-label="moon"> </span>)
    case 'default':
      return (<span role="img" aria-label="sun">☀️ </span>)
    default:
      return mode
  }
}

export default function Layout({ children }) 
{

  const [theme, setTheme] = useColorMode()
  const [mode, setMode] = useColorMode()

  const cycleTheme = (e) => 
  {
    const i = themes.indexOf(theme)
    const next = themes[(i + 1) % themes.length]
    setTheme(next)
  }

  const cycleMode = (e) => 
  {
    const i = modes.indexOf(mode)
    const next = modes[(i + 1) % modes.length]
    setMode(next)
  }

  return (
    <Styled.root>
          <ButtonUI
            sx={{
              ml: 2,
            }}
            onClick={cycleTheme}>
            <span role="img" aria-label="theme"> </span>
            {getThemeName(theme)}
          </ButtonUI>
          <ButtonUI
            sx={{
              ml: 2,
            }}
            onClick={cycleMode}>
            {getModeName(mode)}
          </ButtonUI>
    </Styled.root>
  )
}

Let's merge the second code into the first code, and I renamed const to function, since I can not put const into another const or modify export (because children needs to be defined):

/** @jsx jsx */

import { jsx, Styled, useColorMode } from 'theme-ui'
import {Context} from '../../common'
import SelectLanguage from './SelectLanguage'
import {Links} from './styles'
import {Navegador, StyledHeader} from './styles'
import ButtonUI from '../../theme-ui/button-ui'

const themes = ['deep', 'funk', 'future', 'swiss']
const modes = ['default', 'dark']

const getThemeName = (theme) => 
{
  switch (theme)
  {
    case 'deep':
      return 'Deep'
    case 'funk':
      return 'Funk'
    case 'future':
      return 'Future'
    case 'swiss':
      return 'Swiss'
    default:
        return theme
  }
}

const getModeName = (mode) => 
{
  switch (mode)
  {
    case 'dark':
      return (<span role="img" aria-label="moon"> </span>)
    case 'default':
      return (<span role="img" aria-label="sun">☀️ </span>)
    default:
      return mode
  }
}

const [theme, setTheme] = useColorMode();
const [mode, setMode] = useColorMode();

function cycleTheme (e) {
  const i = themes.indexOf(theme)
  const next = themes[(i + 1) % themes.length]
  setTheme(next)
}

function cycleMode (e) {
  const i = modes.indexOf(mode)
  const next = modes[(i + 1) % modes.length]
  setMode(next)
}

function Header () {
  return (

    <Styled.root>
      <Context.Consumer>
        {({ toggleLanguage, lang }) => (
          <StyledHeader>
            <Navegador id="menu">
              <li>
                <ButtonUI
                  sx={{
                    ml: 2,
                  }}
                  onClick={cycleTheme}>
                  <span role="img" aria-label="theme"> </span>
                  {getThemeName(theme)}
                </ButtonUI>
                <ButtonUI
                  sx={{
                    ml: 2,
                  }}
                  onClick={cycleMode}>
                  {getModeName(mode)}
                </ButtonUI>
                <Links>
                  <SelectLanguage lang={lang} toggleLanguage={toggleLanguage} />
                </Links>
              </li>
            </Navegador>
          </StyledHeader>
        )}
      </Context.Consumer>
    </Styled.root>
  )
}

export default Header

The errors appeared:

Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/warnings/invalid-hook-call-warning.html for tips about how to debug and fix this problem.
resolveDispatcher
node_modules/react/cjs/react.development.js:1465
useContext
node_modules/react/cjs/react.development.js:1473
patch/React$$1.useContext
node_modules/react-hot-loader/dist/react-hot-loader.development.js:2939
useThemeUI
node_modules/@theme-ui/core/dist/index.esm.js:40
useColorMode
node_modules/@theme-ui/color-modes/dist/index.esm.js:169
./src/components/theme/Header/index.jsx/<
src/components/theme/Header/index.jsx:43

  40 |   }
  41 | }
  42 | 
> 43 | const [theme, setTheme] = useColorMode();
  44 | const [mode, setMode] = useColorMode();
  45 | 
  46 | function cycleTheme (e) {

./src/components/theme/Header/index.jsx

You can download small folder of my repo: http://sendanywhe.re/HQ19EMJK or https://filetransfer.io/data-package/i03Or3CD or https://fromsmash.com/Gatsby-i81n-Starter. Use index-example.js.

Oo'-
  • 203
  • 6
  • 22

1 Answers1

1

You could put all of the Layout functionality into your Header component, but why are doing all of this?

// Above this is your imports and various constants
function Header () {
  const [theme, setTheme] = useColorMode();
  const [mode, setMode] = useColorMode();

  function cycleTheme (e) {
    const i = themes.indexOf(theme)
    const next = themes[(i + 1) % themes.length]
    setTheme(next)
  }

  function cycleMode (e) {
    const i = modes.indexOf(mode)
    const next = modes[(i + 1) % modes.length]
    setMode(next)
  }
  
  return (

    <Styled.root>
      <Context.Consumer>
        {({ toggleLanguage, lang }) => (
          <StyledHeader>
            <Navegador id="menu">
              <li>
                <ButtonUI
                  sx={{
                    ml: 2,
                  }}
                  onClick={cycleTheme}>
                  <span role="img" aria-label="theme"> </span>
                  {getThemeName(theme)}
                </ButtonUI>
                <ButtonUI
                  sx={{
                    ml: 2,
                  }}
                  onClick={cycleMode}>
                  {getModeName(mode)}
                </ButtonUI>
                <Links>
                  <SelectLanguage lang={lang} toggleLanguage={toggleLanguage} />
                </Links>
              </li>
            </Navegador>
          </StyledHeader>
        )}
      </Context.Consumer>
    </Styled.root>
  )
}

export default Header;
segFault
  • 3,887
  • 1
  • 19
  • 31