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

How to use React Context with useState hook to share state from different components?

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

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

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

好像menuActive总是在打印1。我可以确保状态在Menu.js内部正常工作,但我不知道如何将状态传递给Layout.js ].

有什么建议,谢谢!

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

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 }

现在您可以像

一样在Layout中使用它
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

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 }

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


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

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