How to use React Context with useState hook to share state from different components?
Answer a question
I am new to React and currently I'm working on a Gatsby site where I have a Layout.js(Parent) and Menu.js(Child), when the state changes on Menu, I'd like it passed to Layout.js.
What I am trying to do is when the menu is active, the text in the layout will change.
Menu.js
import React, {useState, createContext} from "react"
const MenuContext = createContext(1)
const Menu = (props) => {
const [active, setActive] = useState(1)
const clickHandler = () => {
setActive(!active);
}
return(
<div className={(active ? `open` : `close`)} onClick={clickHandler}></div>
)
}
export { Menu, MenuContext }
Layout.js
import React, {useContext} from "react"
import { Menu, MenuContext } from "../components/menu"
const Layout = ({ children }) => {
const menuActive = useContext(MenuContext)
return (
<>
<h1 style={{color:`#fff`}}>{(menuActive) ? `Menu Opened` : `Menu Closed`}</h1>
<main>{children}</main>
<Menu />
</>
)
}
export default Layout
It seems like menuActive is always printing 1. I can make sure the state is working fine inside Menu.js, but I don't know how to pass the state to Layout.js.
Any advices please, thank you!
Answers
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
更多推荐
所有评论(0)