Нажатие кнопки не переключает компонент (когда кнопка и компонент находятся в двух отдельных файлах)

Мой файл макета:

import Menu from "./Menu";
import ButtonMenu from "./ButtonMenu";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang = "en">
      <body className = "grid h-screen grid-cols-[repeat(4,min-content)] overflow-x-scroll">
        <Menu />
        <Pane1 />
        <Pane2 />
        <Pane3 />
        <div className = "w-[100vw]">
          <ButtonMenu />
          <main>{children}</main>
        </div>
      </body>
    </html>
  );
}

Файл моего меню:

"use client";
import { useState } from "react";

export default function Menu() {
  const [open, setOpen] = useState(true);
  return (
    <div
      className = {` overflow-x-hidden overflow-y-scroll ${ open ? "w-[5vw]" : "w-[20vw]" }`}
    >
      <button type = "button" onClick = {() => setOpen(!open)}>
       ...
      </button>
      <nav><ul><li></li></ul></nav>
    </div>
  );
}

Мой файл ButtonMenu:

"use client";
import { useState } from "react";

export default function OpenMenu() {
  const [open, setOpen] = useState(true);
  return (
    <div>
      <button type = "button" onClick = {() => setOpen(!open)}>
       ...
      </button>
    </div>
  );
}

Проблема у меня простая. Когда вы нажимаете на button (событие onClick), которое находится в файле ButtonMenu, Menu не открывается/закрывается и ничего не происходит, ПОЧЕМУ?. Компонент Menu представляет собой отдельный файл. Файл Menu и файл ButtonMenu — это два компонента внутри моего файла Layout. Почему ButtonMenu не открывает/закрывает Menu?

🤔 А знаете ли вы, что...
React Testing Library - это инструмент для тестирования компонентов React.


1
50
1

Ответ:

Решено

Если вы хотите изменить что-то внутри компонента Menu в соответствии с переменной состояния , она должна быть определена внутри него или передана ему как часть его реквизита. open, который вы создали в ButtonMenu, не может иметь никакого влияния на Menu.

Если вы хотите управлять меню из разных компонентов, вы можете использовать контекст . Для этого сначала создайте файл ниже:

// app/MenuContextProvider.tsx

"use client";

import { createContext, useContext, useState } from "react";

const MenuContext = createContext<{ open: boolean; setOpen: Function }>({
  open: false,
  setOpen: () => {},
});

export default function MenuContextProvider({ children }: { children: React.ReactNode }) {
  const [open, setOpen] = useState(true);
  return <MenuContext.Provider value = {{ open, setOpen }}>{children}</MenuContext.Provider>;
}

export function useMenuContext() {
  return useContext(MenuContext);
}

Измените свой Layout.tsx на:

import Menu from "./Menu";
import ButtonMenu from "./ButtonMenu";
import MenuContextProvider from "./MenuContextProvider"
export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang = "en">
      <body className = "grid h-screen grid-cols-[repeat(4,min-content)] overflow-x-scroll">
      <MenuContextProvider>
        <Menu />
        <Pane1 />
        <Pane2 />
        <Pane3 />
        <div className = "w-[100vw]">
          <ButtonMenu />
          <main>{children}</main>
        </div>
       </MenuContextProvider>
      </body>
    </html>
  );
}

Компонент Menu.tsx для:

"use client";
import { useMenuContext } from "./MenuContextProvider"
export default function Menu() {
  const {open, setOpen} = useMenuContext();
  return (
    <div
      className = {` overflow-x-hidden overflow-y-scroll ${ open ? "w-[5vw]" : "w-[20vw]" }`}
    >
      <button type = "button" onClick = {() => setOpen(!open)}>
       ...
      </button>
      <nav><ul><li></li></ul></nav>
    </div>
  );
}

И, наконец, ваш ButtonMenu.tsx, чтобы:

"use client";
import { useMenuContext } from "./MenuContextProvider"
export default function OpenMenu() {
  const {open, setOpen} = useMenuContext();
  return (
    <div>
      <button type = "button" onClick = {() => setOpen(!open)}>
       ...
      </button>
    </div>
  );
}