score:20

Accepted answer

You need to have a Provider that wraps your App before you try to access the context values. In order to have a global and single provider, you need to export wrapRootElement instance from the gatsby-browser.js file. It would look like

MenuContext.js

import React, { createContext, useState } from "react"

export const MenuContext = createContext()

export const MenuProvider = ({ children }) => {
  const [active, setActive] = useState(true);
  return (
    <MenuContext.Provider value={{active,setActive}}>
      {children}
    </MenuContext.Provider>
  );
};

gatsby-browser.js

import React, { useState } from 'react';
import MenuContext from './src/context/MenuContext';
const wrapRootElement = ({ element }) => {
  return (
      <MenuProvider>
        {element}
      </MenuProvider>
  );
};
export { wrapRootElement }

Now you could use it within Layout like

import React, { useContext } from "react"
import { Menu } from "../components/menu"
import { MenuContext } from '../menuContext';
const Layout = ({ children }) => {

  const {active} = useContext(MenuContext)

  return (
    <>
      <h1 style={{color:`#fff`}}>{(active) ? `Menu Opened` : `Menu Closed`}</h1>
      <main>{children}</main>
      <Menu />
    </>
  )
}

export default Layout

and within Menu you would have

import React, { useContext } from "react"
import  { MenuContext } from '../context/MenuContext';

const Menu = (props) => {
  const {active, setActive} = useContext(MenuContext)
  const clickHandler = () => {
    setActive(!active);
  }
  return(
    <div className={(active ? `open` : `close`)} onClick={clickHandler}></div>
  )
}

export { Menu }

Note: You need to create and export the context from a separate file to avoid any circular dependency


However, what you want to achieve can be done without the use of context provided you just to communicate between layout and Menu by lifting the state up to the Layout component

Menu.js

import React from "react"

const Menu = ({clickHandler, active}) => {
  return(
    <div className={(active ? `open` : `close`)} onClick={clickHandler}></div>
  )
}

export { Menu }

Layout.js

import React, {useState} from "react"
import { Menu } from "../components/menu"

const Layout = ({ children }) => {

  const [active, setActive] = useState(1)
  const clickHandler = () => {
    setActive(!active);
  }

  return (
    <>
      <h1 style={{color:`#fff`}}>{(menuActive) ? `Menu Opened` : `Menu Closed`}</h1>
      <main>{children}</main>
      <Menu clickHandler={clickHandler} active={active}/>
    </>
  )
}

export default Layout
  

Related Query

More Query from same tag