简体   繁体   English

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

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

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

What I am trying to do is when the menu is active, the text in the layout will change.我想要做的是当菜单处于活动状态时,布局中的文本会发生变化。

Menu.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 }

Layout.js布局.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.似乎menuActive总是打印 1。我可以确保 Menu.js 中的状态工作正常,但我不知道如何将状态传递给 Layout.js。

Any advices please, thank you!请大家给点建议,谢谢!

You need to have a Provider that wraps your App before you try to access the context values.在尝试访问上下文值之前,您需要有一个包装您的应用程序的 Provider。 In order to have a global and single provider, you need to export wrapRootElement instance from the gatsby-browser.js file.为了拥有一个全局和单一的提供者,你需要从 gatsby-browser.js 文件中导出wrapRootElement实例。 It would look like它看起来像

MenuContext.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>
  );
};

gatsby-browser.js 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现在您可以在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

and within Menu you would haveMenu你会有

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

Menu.js菜单.js

import React from "react"

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

export { Menu }

Layout.js布局.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
  

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM