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..
CodePudding user response:
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>