Как использовать React Context с хуком useState для совместного использования состояния из разных компонентов?

Я новичок в 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 22.07.2019 13:08

Привет, @James, я рассматривал этот метод, но будет больше компонентов, которые будут совместно использоваться в одном и том же состоянии, и я где-то читал, что использование Context было бы намного чище, если бы у меня было несколько вложенных компонентов.

whalesingswee 23.07.2019 05:32
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
11
2
14 579
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вам нужно иметь провайдера, который обертывает ваше приложение, прежде чем вы попытаетесь получить доступ к значениям контекста. Чтобы иметь глобального и единого провайдера, вам нужно экспортировать экземпляр 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.

whalesingswee 23.07.2019 05:46

Можете ли вы убедиться, что вы экспортировали wrapRootElement из gatsby-browser.js, а также передали значение контекста провайдеру, как указано в ответе

Shubham Khatri 23.07.2019 05:55

Извините, мне потребовалось некоторое время, я перепроверил, и теперь это появляется Unhandled Rejection (Invariant Violation): Invalid hook call. Hooks can only be called inside of the body of a function component. .

whalesingswee 23.07.2019 07:35

Я смог исправить это, переместив <Menu.Provider> внутрь файла контекста и вместо этого оставив файл gatsby-browser.js чистым.

whalesingswee 23.07.2019 09:16

Как вы тестируете такой компонент с помощью шутки?

blueprintchris 10.09.2021 16:08

Другие вопросы по теме