Multiple Buttons with their own Events in ReactJS - javascript

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..

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>

Related

In react, how to pass states down as props?

This is a very noob question but I've been trying all day do implement this. Please help me out.
Sorry for the length, just tried to put out the whole thing I am struggling with
I am trying to build custom buttons and to do so, I created a component so I can create as many buttons that I want. For that I declared a state and passed down some information as props, which is as follows:
import React, {useState} from 'react'
import Button from '../components/Button'
function CustomButton() {
const [clicked, setClicked] = useState(false)
return (
<div className='CustomButton'>
<Navbar />
<Button setClicked={setClicked} name="Button One" clicked={clicked}/>
<Button setClicked={setClicked} name="Button Two" clicked={clicked}/>
<Button setClicked={setClicked} name="Button Three" clicked={clicked}/>
</div>
)
}
export default CustomButton
As you can see, we passed the state and name of that button down. To render this Buttons, following component has been created:
import React from 'react'
import Modal from './Modal/Modal'
function Button({setClicked, name, clicked}) {
return (
<div>
<button onClick={() => {setClicked(true)}}>{name}</button>
{clicked && <Modal closeModal={setClicked} name={`You Clicked ${name}`} />}
</div>
)
}
export default Button
And lastly, when once a button is clicked, we want to perform some action. That action is to pop the Modal on a screen. And to do so, we created a Modal and passed down few props. Code for the same is as follows:
import React from 'react'
function Modal({closeModal, name}) {
return (
<div className='modal'>
<div className='modalContainer'>
<p>{name}</p>
<div>
<button onClick={() => {closeModal(false)}}>×</button>
</div>
</div>
</div>
)
}
export default Modal
The expected result is for a Modal to pop with "You clicked button One", supposing we clicked one something similar to this.
The actual result is that all three Modals pop up one above the other when any of the three buttons are passed. The result:
I realize that I am passing the states wrong way. When any of the button is clicked all three get set to true. I simply don't realize how. Don't they create a method for each one?
Also, can you guys please teach me a better/understandable way to write clicked logic. Like maybe
if(clicked){
<Modal closeModal={setClicked} name={`You Clicked ${name}`} />
}
Because you bind all three buttons with one state, You need a state as array, with items equal to the number of buttons.
const [clicked, setClicked] = useState([false, false, false])
return (
<div className='CustomButton'>
<Navbar />
{
clicked.map((button, i) => {
return <Button setClicked={setClicked} name="Button Three" clicked={clicked[i]} index={i}/>
})
}
</div>
)
Then in the button component.
function Button({setClicked, name, clicked, index}) {
return (
<div>
<button onClick={() => {setClicked(prev => prev.map((item, i) => {
return i === index ? true : item
}))}}>{name}</button>
{clicked && <Modal closeModal={setClicked} name={`You Clicked ${name}`} />}
</div>
)
}
And the modal component.
function Modal({ closeModal, name, index }) {
return (
<div className="modal">
<div className="modalContainer">
<p>{name}</p>
<div>
<button
onClick={() => {
closeModal((prev) =>
prev.map((item, i) => {
return i === index ? false : item;
})
);
}}
>
×
</button>
</div>
</div>
</div>
);
}
You can find a working example on this link.
https://codesandbox.io/s/old-wood-zgjno9
You can implement multiple modals like this:
import { useState } from "react";
export default function App() {
const [showModal1, setShowModal1] = useState(false);
const [showModal2, setShowModal2] = useState(false);
return (
<div className="App">
<button onClick={(e) => setShowModal1(true)}>Button 1</button>
<button onClick={(e) => setShowModal2(true)}>Button 2</button>
{showModal1 && (
<Modal text="Modal 1" onClose={(e) => setShowModal1(false)} />
)}
{showModal2 && (
<Modal text="Modal 2" onClose={(e) => setShowModal2(false)} />
)}
</div>
);
}
const Modal = ({ text, onClose }) => {
return (
<div>
{text}
<button onClick={onClose}>Close</button>
</div>
);
};
Working example

How to change cart button "ADD" to some other name

I want to make the "ADD" button to " ADDED" after I clicked the ADD button and make it disabled.
{
products.map((val, i)=>(
<div className="product_item">
{
val.productType === 'chicken'?
<>
<img src={img2}></img>
<p className="product_data">Chicken Name: {val.chickeName}</p>
<p align="left" className="product_data">weight: 500gm</p>
<p align="left"className="product_data">₹{val.price}/- <button onClick={addTocartProduct}>ADD</button></p>
</>: null
}
</div>
))
}
I have sample project in react js having add to cart button is there. But I want to make the "ADD" button to " ADDED" after I clicked the ADD button and make it disabled.
If anyone can know please, drop your answer.
Considering that you have an array of products you should initialize an array of bool:
const [buttonsClicked, setButtonsClicked] = useState(new Array(products).fill(false));
Then
{
products.map((val, i)=>(
<div className="product_item">
{
val.productType === 'chicken'?
<>
<img src={img2}></img>
<p className="product_data">Chicken Name: {val.chickeName}</p>
<p align="left" className="product_data">weight: 500gm</p>
<p align="left"className="product_data">₹{val.price}/- <button onClick={(e) => addTocartProduct(val, i)}>{buttonsClicked[i] ? 'ADDED' : 'ADD'}</button></p>
</>: null
}
</div>
))
}
And finally the addTocartProduct function:
const addTocartProduct = (val , i) => {
let result = [...buttonsClicked];
result[i] = !result[i];
setButtonsClicked(result);
}
Youjust need a simple ternary condition and a state. Here is an example :
const App = () => {
const [isAdded, setIsAdded] = React.useState(false);
const handleClick = () => {
setIsAdded(true);
}
return (
<div>
<button onClick={handleClick}>{isAdded ? 'ADDED' : 'ADD'}</button>
</div>
);
};
You have to define some state in your component
const [ctaButton,setCtaButton] = useState('ADD')
Then in your addTocartProduct function set the state to new one
setCtaButton('ADDED')
Finally
<button onClick={addTocartProduct}>{ctaButton}</button>

how to toggle between two css classes view types with react

I have a List and Grid type display. How do I toggle between them in React. I want to toggle between jsGridView and jsListView classes.
This is the vanilla js of the toggling of classes
const listView = document.querySelector('.list-view');
const gridView = document.querySelector('.grid-view');
const projectsList = document.querySelector('.project-boxes');
listView.addEventListener('click', () => {
gridView.classList.remove('active');
listView.classList.add('active');
projectsList.classList.remove('jsGridView');
projectsList.classList.add('jsListView');
});
gridView.addEventListener('click', () => {
gridView.classList.add('active');
listView.classList.remove('active');
projectsList.classList.remove('jsListView');
projectsList.classList.add('jsGridView');
});
** this is my react file where I have the display items and buttons to toggle. how do I implement the toggle event listeners into my react file**
How do I toggle between the two classes - jsGridVew and jsListView
const [isActive, setIsActive] = useState(false)
const listToggle = () => {
setIsActive(!isActive)
}
<button key={isActive} className="view-btn list-view" title="List View" onClick={listToggle}>
<i className="fal fa-list-ul fa-2x"></i>
</button>
<button className="view-btn grid-view active" title="Grid View">
<i className="fal fa-th-large fa-2x"></i>
</button>
<div className="project-boxes jsGridView">
{!loading && records.length === 0 ? (<h4 style={{ margin: '20px' }} className='center'>No
records, sorry</h4>) : records.map((record, key) => (
<RecordItem key={key} record={record} isFilter={isFilter} filterByWhat={filterByWhat} />
))}
</div>
EDIT: > I also want to add an 'active class on each button on click. I've tried somethings but it doesn't work
I am assuming this div is where you want to toggle between jsGridView and jsListView
<div className="project-boxes jsGridView">
So why not use a state variable to store the class name? Then use the onClick even to set it.
const [cName, setClassName] = useState('jsGridView');
return (
<Fragment>
<button className="view-btn list-view" title="List View" onClick={() => setClassName('jsListView')}>
List View
</button>
<button className="view-btn list-view" title="Grid View" onClick={() => setClassName('jsGridView')}>
Grid View
</button>
<div className={"project-boxes "+cName}>
{!loading && records.length === 0 ? (<h4 style={{ margin: '20px' }} className='center'>No
records, sorry</h4>) : records.map((record, key) => (
<RecordItem key={key} record={record} isFilter={isFilter} filterByWhat={filterByWhat} />
))}
</div>
</Fragment>
)
So here you set your class to jsGridView initially so it renders in grid view by default. But you also have 2 buttons that can flip it between grid and list view.
You can also add an active class to the button if you want.
<button className={"view-btn list-view"+(cName === 'jsListView' ? ' active_btn':'')} title="List View" onClick={() => setClassName('jsListView')}>
List View
</button>
<button className={"view-btn list-view"+(cName === 'jsGridView' ? ' active_btn':'')} title="Grid View" onClick={() => setClassName('jsGridView')}>
Grid View
</button>
If one class is on, and the other is off, you can do
function toggleClass(elem) {
const classList = elem.classList;
classList.toggle('class1');
classList.toggle('class2');
}
Now the on/off status of the two classes is reversed
Also, in your Styles / CSS file, you can add :not on one class and then not need to use the other class, like
#elem.class1 {
color: ...;
font-size: ...;
}
#elem.class2, #elem:not(.class1) {
color: ...;
font-size: ...;
}
So that :not(.class1) has the same styling effects as adding class2
In React, it's uncommon to mix "vanilla" imperative JS and React like this. React provides a clean solution for toggling displayed elements that I would advise you use instead.
Take the following as an example:
// list-view.jsx
const ListView = ({ items=[] }) => {
const itemsElements = items.map(item => {
return (<li>{item}</li>)
});
return (<ul>{itemsElements}</ul>)
}
// grid-view.jsx
const GridView = ({ items=[] }) => {
const itemsElements = items.map(item => {
return (<span>{item} </span>)
});
return (<div>{itemsElements}</div>)
}
// list-grid-view.jsx
const ListGridView = ({ items=[] }) => {
const [listView, setListView] = React.useState(true);
// this fn toggles the listView variable
const toggleListView = React.useCallback(() => {
setListView(!listView);
}, [listView, setListView]);
return (
<div>
<button onClick={toggleListView} >Toggle!</button>
{listView ? <ListView items={items} /> : <GridView items={items} />}
</div>
);
}
const items = ['Hello', 'World', '!'];
const element = <ListGridView items={items} />
ReactDOM.render(element, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Next.js show/hide divs on buttons

I am trying to show and hide divs on buttons in Next JS.
This post helped me a lot:
Next.js toggle display of a div tag
I used this solution using useState in my case and it works perfectly. The difference is that I have many buttons and each of them supposes to show different div and hide active div.
This is what I am trying to achieve
https://codepen.io/CthuKi/pen/YqZvRv
export default function Header(){
const [showMe, setShowMe] = useState(false);
function toggle(){
setShowMe(!showMe);
}
return (
<>
<button onClick={toggle}>Button 1</button>
<button onClick={toggle}>Button 2</button>
<button onClick={toggle}>Button 3</button>
<div>
<div style={{display: showMe?"block":"none"}}>
Show this div on button 1 and hide other active
</div>
<div style={{display: showMe?"block":"none"}}>
Show on button 2 and hide other active
</div>
<div style={{display: showMe?"block":"none" }}>
Show on button 3 and hide other active
</div>
</div>
</>
);
}
I will appreciate any help
One solution would be to have multiple toggle functions to toggle multiple divs. But there is a better way, create only one state variable showAll and one function toggleAll to show all the content.
To toggle one button to show specific content, create an array of data that you want to show, and just change the index to show the current data from the array.
For example, see the codesandbox for demo
import React, { useState } from "react";
export default () => {
const [data, setData] = useState(["hello", "hi there", "holla"]);
const [showAll, setShowAll] = useState(false);
const [currentIdx, setCurrentIdx] = useState(0);
const [showCurrent, setShowCurrent] = useState(false);
const toggleAll = () => {
setShowAll(val => !val);
setShowCurrent(false);
};
const toggleCurrent = () => {
if (!showCurrent) {
setShowCurrent(true);
setShowAll(false);
return;
}
};
const setCurrent = index => {
setCurrentIdx(index);
toggleCurrent();
};
return (
<div>
<div>
<button onClick={toggleAll}>{showAll ? "Hide All" : "Show All"}</button>
<button onClick={() => setCurrent(0)}>First</button>
<button onClick={() => setCurrent(1)}>Second</button>
<button onClick={() => setCurrent(2)}>Third</button>
</div>
<div>
{showAll && data.map((el, i) => <p key={`content-${i}`}>{el}</p>)}
</div>
{showCurrent ? <div>{data[currentIdx]}</div> : null}
</div>
);
};

How do I get the value from a React Img component

I keep getting undefined from the console.log in 'handleClickVideo'. How can I get the value out of clicking on a video properly? I tried with a div also however div doesn't have a value property. I thought Img did though.
const Videos = ({ videos }) => {
const handleClickVideo = (event) => {
console.log(event.target.value)
}
return (
<>
<h2 className="title is-5">Videos</h2>
<div className="columns is-multiline">
<VideoModal
videoOpen={videoOpen}
setVideoClose={handleClickVideo}
/>
{
videos.map((video, key) => {
return (
<div className="column is-one-third">
<div className={styles.thumbnail}>
<Img src={`https://img.youtube.com/vi/${video.link}/0.jpg`} onClick={handleClickVideo} value={video.link}/>
</div>
<p className={styles.videoTitle}>Green Book Official Trailer</p>
</div>
)
})
}
</div>
</>
)
}
You're calling handleClickVideo in VideoModal but VideoModal doesn't have any value, so it will be undefined in your callback
<VideoModal
videoOpen={videoOpen}
setVideoClose={handleClickVideo}
/>
You can make your callback function to accept a value:
const handleClickVideo = (video) => {
console.log(video)
}
And then update your render function:
<VideoModal
videoOpen={videoOpen}
setVideoClose={() => handleClickVideo(0)}
/>
<Img
src={`https://img.youtube.com/vi/${video.link}/0.jpg`}
onClick={()=>handleClickVideo(video.link)}
/>

Categories