0

What would be the best way to handle multiple buttons that show/hide their respective DIVs.

Example of the Code I have below. As you can see it's set to handle just the first button. I need a method to continually add more buttons that show/hide their own DIVs.

Example: When Button(A) is clicked DIV(A) fades in. If Button(A) is clicked again DIV(A) fades out. If/instead Button(B) is clicked and DIV(A) is visible, then DIV(A) fades out and DIV(B) fades in. so on.. etc..

const Example2: React.FC = () => {      
  const [style, setStyle] = useState("invis");

  const changeStyle = () => {
    console.log("you just clicked");

    setStyle("vis");
  };  

  return (        
    <Container className="content">                    
      <section className="section">                 
        <div className="video_section">
          <video src={videoUrl} width="" height ="" loop autoPlay muted> </video>
          <div className="video_words">
            <p><span>Video Words</span></p>
            <h1>
              <span>V</span>
              <span>I</span>
              <span>D</span>
              <span>E</span>
              <span>O</span>
            </h1> 
            <div className="TierTwo">
              <div className="Top1">
                <button type="button" onClick={changeStyle} className="Lrow1">
                  <img src="/img/image1.png" height="auto" width="auto" />                                 
                </button>
                <div className={style}>
                  <One />                                 
                </div>
                <div className={style}>
                  <Two />      
                </div>                                  
                <div className="Rrow1">
                  <button type="button" onClick={changeStyle} className="Rrow1">
                    <img src="/img/image2.png" height="auto" width="auto" />
                  </button>
                </div>
              </div>
              <div className="Top2">
                <button type="button" onClick={changeStyle} className="Lrow2">
                  <img src="/img/image3.png" height="auto" width="auto" />                                 
                </button>
                  <div className={style}>
                    <Three />                                 
                  </div>
                  <div className={style}>
                    <Four />      
                  </div>                                  
                  <div className="Rrow1">
                    <button type="button" onClick={changeStyle} className="Rrow2">
                      <img src="/img/image4.png" height="auto" width="auto" />
                    </button>
                  </div>
                </div>
                <div className="Top2">
                  ..etc..

1 Answer 1

1

As we want there only exists only one state that manage multiple buttons, such that no manual useState declaration, thus, we consider there is only a single state style, and it is an object, the key of style would be the button name or index, and by using changeStyle(index)() should trigger the onclick logic for button[index], for achieving the pattern like below:

(
  <button onClick={changeStyle(1)}>button1</button>
  <button onClick={changeStyle(2)}>button2</button>
  <button onClick={changeStyle(3)}>button3</button>

  <div>{style[1]}</div>
  <div>{style[2]}</div>
  <div>{style[3]}</div>
)

Since we want changeStyle(index) would by default set the state style[index] to "invis".

Hence will suppose the changeStyle function will be look

const changeStyle = (index) => {
  // set style[index] to "invis" by function setStyle

  // onclick function goes here
  return (event) => {
    // this block will be executed on every onclick
  }
}

And since we leveraged the setStyle function inside a IIFE to assign default value of each index, therefore, once we successfully set, we should design a mechanism that would stop constantly trigger it again, or else a forever render-loop will be happened.

const changeStyle = (index) => {
  if (!style[index]) {
    // setStyle goes here
  }
  // ...
}

And in order to improve atomicity of each setStyle operation, we should pass a callback function to the setStyle function instead of a value.

const changeStyle = (index) => {
  if (...) {
    setStyle((style) => {
      ...style,
      [index]: "invis",
    });
  }
  // ...
}

The handy function is done, now considering the onclick logic.

Regarding the logic of showing and hiding:

  1. apply all style to invis regardless of their current states
  2. toggle current style[index]: if it is "invis", then set it to "vis", vice versa.

hence, the setStyle function in onclick function should looks like:

setStyle((style) => ({
  // set all style to "invis"
  ...(
    Object.keys(style)
      .reduce((a, b) => ({
        ...a,
        [b]: "invis"
      }), {})
  ),
  // toggle current index state
  [index]: style[index] === "invis" ? "vis" : "invis",
}));

Complete solution:

const { useState } = React;

function App() {

  // the type of style will be { [key: any]: "invis" | "vis" }
  const [style, setStyle] = useState({});

  // IIFE for filling default value "invis"
  // return a callback when onclick happened
  const changeStyle = (index) => {
    // this will be run for the amount of button you have
    if (!style[index]) {
      setStyle((style) => ({
        ...style,
        [index]: "invis",
      }));
    }

    // for every onclick, this function will be run
    return () => {
      setStyle((style) => ({
        ...(
          Object.keys(style)
            .reduce((a, b) => ({
              ...a,
              [b]: "invis"
            }), {})
        ),
        [index]: style[index] === "invis" ? "vis" : "invis",
      }));
    };
  };

  return (
    <div className="App">
      <button onClick={changeStyle(1)}>button1</button>
      <button onClick={changeStyle(2)}>button2</button>
      <button onClick={changeStyle(3)}>button3</button>

      <div>{style[1]}</div>
      <div>{style[2]}</div>
      <div>{style[3]}</div>
    </div>
  );
}

ReactDOM.render(
    <App />,
    document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.7.0-alpha.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.7.0-alpha.0/umd/react-dom.production.min.js"></script>

<div id="root"></div>

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.