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..
Advertisement
Answer
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:
- apply all style to invis regardless of their current states
- 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>