Search code examples
reactjsnext.jsreact-context

onClick button switches between two components (react context)


I am struggling to write SwitchContext and/or SwitchButton so that the SwitchButton makes toggling between components MenuOne and MenuTwo happen.

I tried to put an IF ELSE statement in SwitchContext with a value, and then when that value is true (in SwitchButton), it would switch to MenuOne and when false then switch to MenuTwo. But that did not work. How to make it?

Layout.tsx:

<div>
 <SwitchContext>
  <SwitchButton />
   <div><!-- switch MenuOne <> MenuTwo --></div>
 </SwitchContext>
</div>

MenuOne (and MenuTwo is the same but with different content):

"use client";
export default function MenuOne() {
  return (
    <div>
      <p>MenuOne</p>
    <div>);}

SwitchContext.tsx: (Question: How to write SwitchContext.tsx so that toggling happens between component MenuOne and MenuTwo when clicking SwitchButton?):

"use client";
import MenuOne from "./MenuOne";
import MenuTwo from "./MenuTwo";
import { createContext, useContext, useState } from "react";

const SwitchContext = createContext(false);

export default function SwitchContext({children,}: {children: React.ReactNode;}) {
  const [switch, setSwitch] = useState(true);
  const switchFunction =  ()  =>  {
    setSwitch(!switch);
  return (
    <SwitchContext.Provider value={{ switch, switchFunction }}>
      {children}
    </SwitchContext.Provider>
  );
}
export function useSwitchContext() {
  return useContext(SwitchContext);
}

SwitchButton (Question: How to write SwitchButton.tsx so that toggling happens between component MenuOne and MenuTwo when clicking SwitchButton?):

"use client";
import MenuOne from "./MenuOne";
import MenuTwo from "./MenuTwo";

export default function Switch() {
  const { switch, setSwitch } = useSwitchContext();
  return (
    <button type="button" onClick={() => setSwitch(!switch)}</button>
  );
}

Solution

  • Your mindset of creating SwitchContext.tsx is utterly correct. But I couldn't understand why you imported the MenuOne and the MenuTwo there!

    Also, your initial value isn't aligned with your pass value, and you have a curly brace syntax mistake. I'll fix both now:

    import { createContext, useContext, useState } from "react";
    
    type SwitchContextType = {
      switch: boolean;
      switchFunction: () => void;
    };
    
    // Here is the initial value issue
    const SwitchContext = createContext<SwitchContextType>({
      switch: false,
      switchFunction: () => {},
    });
    
    export default function SwitchContext({children,}: {children: React.ReactNode;}) {
      const [switch, setSwitch] = useState(true);
    
      const switchFunction =  ()  =>  {
        setSwitch(!switch);
      }; // Here is the missed curly brace
    
      return (
        <SwitchContext.Provider value={{ switch, switchFunction }}>
          {children}
        </SwitchContext.Provider>
      );
    };
    
    export function useSwitchContext() {
      return useContext(SwitchContext);
    }; // By this lovely hook, you will pass the switch and the
    // switchFunction everywhere, nothing else!
    

    And inside the switch button should be exactly like the following, not the one you wrote:

    // Still here, it's not needed to import the MenuOne or MenuTwo
    
    export default function Switch() {
      const { switchFunction } = useSwitchContext();
    
      return (
        <button type="button" onClick={switchFunction}</button>
      );
    }
    

    Now, it is time to toggle between the MenuOne and the MenuTwo:

    // Layout.tsx
    
    import MenuOne from "./MenuOne";
    import MenuTwo from "./MenuTwo";
    
    const Layout = () => {
      const { switch } = useSwitchContext();
      const Menu = switch ? MenuOne : MenuTwo;
    
      return (
        <>
          <div>
            <Menu />
          </div>
        </>
      );
    };
    

    The above code Layout.tsx is a sample code to show you how to use the useSwitchContext and how to toggle between MenuOne and MenuTwo to render.