Я новичок в 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.
Любые советы, пожалуйста, спасибо!
Привет, @James, я рассматривал этот метод, но будет больше компонентов, которые будут совместно использоваться в одном и том же состоянии, и я где-то читал, что использование Context было бы намного чище, если бы у меня было несколько вложенных компонентов.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Вам нужно иметь провайдера, который обертывает ваше приложение, прежде чем вы попытаетесь получить доступ к значениям контекста. Чтобы иметь глобального и единого провайдера, вам нужно экспортировать экземпляр wrapRootElement из файла gatsby-browser.js. Это будет выглядеть как
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 }
Примечание. Вам необходимо создать и экспортировать контекст из отдельного файла, чтобы избежать циклической зависимости.
Однако то, чего вы хотите достичь, можно сделать без использования контекста, если вы просто взаимодействуете между макетом и меню, поднимая состояние до компонента макета.
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
Спасибо! Мне удалось решить проблему с помощью метода Lift State Up, однако с более сложными компонентами, которые имеют одно и то же состояние, мне нужно лучше понять использование Context API с вашим решением, но, похоже, я не смог заставить его работать. Он продолжает говорить setActive is not a function после срабатывания onClick.
Можете ли вы убедиться, что вы экспортировали wrapRootElement из gatsby-browser.js, а также передали значение контекста провайдеру, как указано в ответе
Извините, мне потребовалось некоторое время, я перепроверил, и теперь это появляется Unhandled Rejection (Invariant Violation): Invalid hook call. Hooks can only be called inside of the body of a function component. .
Я смог исправить это, переместив <Menu.Provider> внутрь файла контекста и вместо этого оставив файл gatsby-browser.js чистым.
Как вы тестируете такой компонент с помощью шутки?
Почему вы просто не используете реквизиты для передачи данных от родителя к дочернему? Есть ли у вас более сложная ситуация, когда состояние необходимо совместно использовать с большим количеством вложенных компонентов?