如何使用带有 useState 钩子的 React Context 来共享来自不同组件的状态?

wha*_*wee 9 javascript reactjs gatsby

我是 React 的新手,目前我正在 Gatsby 站点上工作,在那里我有一个 Layout.js(Parent) 和 Menu.js(Child),当 Menu 上的状态发生变化时,我希望它传递给 Layout.js .

我想要做的是当菜单处于活动状态时,布局中的文本会发生变化。

菜单.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 }
Run Code Online (Sandbox Code Playgroud)

布局.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
Run Code Online (Sandbox Code Playgroud)

似乎menuActive总是打印 1。我可以确保 Menu.js 中的状态工作正常,但我不知道如何将状态传递给 Layout.js。

请大家给点建议,谢谢!

Shu*_*tri 12

在尝试访问上下文值之前,您需要有一个包装您的应用程序的 Provider。为了拥有一个全局和单一的提供者,你需要wrapRootElement从 gatsby-browser.js 文件中导出实例。它看起来像

菜单上下文.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>
  );
};
Run Code Online (Sandbox Code Playgroud)

gatsby-browser.js

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

现在你可以在Layoutlike 中使用它

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
Run Code Online (Sandbox Code Playgroud)

Menu你的内心会有

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 }
Run Code Online (Sandbox Code Playgroud)

注意:您需要从单独的文件中创建和导出上下文以避免任何循环依赖


但是,如果您只是通过将状态提升到 Layout 组件来在布局和菜单之间进行通信,则可以在不使用上下文的情况下完成您想要实现的目标

菜单.js

import React from "react"

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

export { Menu }
Run Code Online (Sandbox Code Playgroud)

布局.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
  
Run Code Online (Sandbox Code Playgroud)