I have created a component in my ReactJS app with a Button and a div. My goal is to press the button and show/hide the div, which currently works. But I will re-use the component so I will have multiple Buttons and divs. But I always only want one div to show.
If I press a button, hide all the divs from the same component and show the div from the button I just pressed, otherwise if the button I just pressed div is open hide it. It work the same as Bootstrap's dropdown button, but I prefer not to use Bootstrap's dropdown as I would like to create my own custom button.
I import the below Hide component in my App.js file. It works by hiding or showing the div, but would like to hide all other open div's apart from the button I currently pressed if it is not open yet.
Here is the code I currently have and use my Mycomp twice,
function hide () {
return (
<div>
<Mycomp />
<Mycomp />
</div>
);
}
function Mycomp(){
const[dp, setDp] = useState("none");
const toggle = () => {
if (dp === "none"){
setDp("block")
}else{
setDp("none")
}
}
return(
<div>
<button onClick={toggle}>Test</button>
<div style={{display: dp}}>{dp}</div>
</div>
)
}
export default hide;
You can change your component this way to get what you want . try to run this code to see the result
function Hide() {
const [visibleDivIndex, setVisibleDivIndex] = React.useState(0);
const handleVisibleDiv = id => setVisibleDivIndex(id);
const divArr = [1, 2, 3]; // just to show haw many component we will use
return (
<div>
{divArr.map((item, index) => (
<Mycomp
key={index}
id={index}
visibleDivIndex={visibleDivIndex}
handleVisibleDiv={handleVisibleDiv}
/>
))}
</div>
);
}
function Mycomp({ id, visibleDivIndex, handleVisibleDiv }) {
return (
<div>
<button onClick={e => handleVisibleDiv(id)}>Test</button>
<div style={{ display: id === visibleDivIndex ? "block" : "none" }}>
My Div Content
</div>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<React.StrictMode>
<Hide />
</React.StrictMode>,
rootElement
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
The basic idea would be to move the state up to the container.
function Hide() {
const defaultDisplay = () => Array.from({length: 2}).fill(false);
const [displays, setDisplays] = useState(defaultDisplay());
function onClick(id) {
const temp = defaultDisplay();
temp[id] = true;
setDisplays(temp);
}
return (
<div>
{
displays.map((display, i) => {
return <Mycomp display={display} id={i} onClick={onClick} />;
}
}
</div>
);
}
function Mycomp({display, id, onClick}) {
const[dp, setDp] = useState(display);
useEffect(() => {
setDp(display ? 'block' : 'none');
}, [display]);
return(
<div>
<button onClick={() => onClick(id)}>Test</button>
<div style={{display: dp}}>{dp}</div>
</div>
)
}
export default Hide;
Related
How can I Change styles on hover of an appropriate item react? Now the styles are changed of all of the items at a time. Hovering on add to cart button I need to change only the chosen div styles.
https://codesandbox.io/s/trusting-moon-djocul?file=/src/components/Category.js**
import React, { useState } from "react";
import styles from "./category.module.css";
import Categories from "./Categories";
const Category = () => {
const [hovered, setHovered] = useState(false);
const [data, setData] = useState(Categories);
const style = hovered
? { backgroundColor: "#ffcbcb", color: "#fff", transition: "all 0.5s ease" }
: {};
const filterResult = (catItem) => {
const result = Categories.filter((curData) => curData.category === catItem);
setData(result);
};
return (
<>
<div className={styles.items}>
{data.map((value) => {
const { id, title, price, description } = value;
return (
<>
<div className={styles.item} key={id} style={style}>
<h1>{title}</h1>
<p>
{price} <span>$</span>
</p>
<p>{description}</p>
<button
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
className={styles.btn}
>
Add to Cart
</button>
</div>
</>
);
};
export default Category;
That's because you have a single "hovered" state shared between all your cards, you should create a "Card" component and have that component have its own hovered state
return (
<>
<div className={styles.items}>
{data.map((value) => {
return (
<>
<Card {...props} />
</>
);
Problem
This issue is occurring cause you are applying CSS to all the cards. The only small thing you are missing in your logic is adding CSS to the only card whose button was being hovered.
Solution
That can be achieved by using event objects on mouse enter and mouse leave events.
<div className={styles.item} key={id} style={style}>
<h1>{title}</h1>
<p>
{price} <span>$</span>
</p>
<p>{description}</p>
<button
onMouseEnter={(e) =>
e.currentTarget.parentElement.classList.add("active_card")
}
onMouseLeave={(e) =>
e.currentTarget.parentElement.classList.remove("active_card")
}
className={styles.btn}
>
Add to Cart
</button>
</div>
This will add a class of "active_card" on the card whose Add To Card button is being hovered, then you can apply your desired styling to that class.
.active_card {
background-color: #ffcbcb !important;
}
Example
Working Code Sandbox Example
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
I am new to learning react and am stuck with this doubt. I have a simple button and on click of that button I want to add some text (or any other html) element. The console log statement is getting executed but the div tag is not getting rednered. This is my following code.
function App() {
const executeMe = () => {
console.log("executed")
return(
<div> Clicked here</div>
)
}
return (
<LocalizationProvider dateAdapter={AdapterDateFns}>
<div className="App">
Hello world
<Button onClick={executeMe}> click me</Button>
</div>
</LocalizationProvider>
);
}
export default App;
I know that I am missing out something which may be very simple. Please help me fix this. Thanks
Your looking at React wrongly, it doesn't work this way. You can do this instead.
import { useState } from "react";
function App() {
const [clicked, setClicked] = useState(false);
const [lines, setLines] = useState([]);
const executeMe = () => setClicked(!clicked);
const onAddLine= () => setLines(lines.concat("New line (Could be unique)"));
return (
<div className="App">
Hello world
{/* METHOD A */}
{!clicked && <button onClick={executeMe }>Click me</button>}
{clicked && <div>Clicked here</div>}
<br />
{/* METHOD B */}
<button onClick={executeMe}>{clicked ? "Clicked here" : "Click me"}</button>
<br />
{/* ADDITIONAL FUN STUFF WITH SEPERATE BUTTON */}
<button onClick={onAddLine}>Add new line</button>
<br />
{lines.map((line, x) => {
return(
<div key = {x}>{x+1} : {line}</div>
);
})}
</div>
);
};
export default App;
You can render that div by using state instead and reset it on the next click.
function App() {
const [showDiv, setShowDiv] = useState(false);
const executeMe = () => {
console.log("executed");
setShowDiv(!showDiv);
};
return (
<LocalizationProvider dateAdapter={AdapterDateFns}>
<div className="App">
Hello world
<Button onClick={executeMe}> click me</Button>
{showDiv && <div> Clicked here</div>} {/* render div once showDiv state is true */}
</div>
</LocalizationProvider>
);
}
export default App;
You should add a state value to check when the button has been pressed.
Here is more information about how to use useState hook.
function App() {
const [isButtonPressed, setIsButtonPressed] = useState(false);
const executeMe = () => {
console.log("executed");
setIsButtonPressed(true);
}
return (
<LocalizationProvider dateAdapter={AdapterDateFns}>
<div className="App">
Hello world
<Button onClick={executeMe}> click me</Button>
{isButtonPressed && <div>Clicked here</div>}
</div>
</LocalizationProvider>
);
}
export default App;
There are many ways to achieve it.
First React is just JavaScript, most JS code will work within the component.
But some dev might find it not so React which is weird for me :)
So here are the two examples that you might try:
function App() {
const [list, setList] = React.useState([])
const handleAddLine = () => {
const lists = document.getElementById('lists')
const li = document.createElement('li')
li.textContent = 'hey'
lists.append(li)
}
const handleAddLineReactish = () => {
setList(prevList => {
return prevList.concat(<li>hey</li>)
})
}
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<button onClick={handleAddLine}>Add</button>
<ul id='lists'></ul>
<button onClick={handleAddLineReactish}>Add Reactish</button>
<ul>
{list.length > 0 && list.map((l, i) => {
return (
<li key={i}>{l}</li>
)
})}
</ul>
</div>
);
}
sandbox URL: https://codesandbox.io/s/funny-sun-7f4epn?file=/src/App.js
For something like this we use a react hook called "useState".
In "useState" we store a something and on the basis of that we do stuff like to show, hide and more.
See the image
you can write that html code in another component and import it into the current file you can make useState to check the value is 'visible' with type 'true/false' to check the state when the button is click.
code example
import React, { useState } from "react";
function App() {
const [showText, setShowText] = useState(false);
const executeMe = () => {
console.log("executed")
setShowText(true);
}
return (
<LocalizationProvider dateAdapter={AdapterDateFns}>
<div className="App">
Hello world
<Button onClick={executeMe}> click me</Button>
{showText ? <Text /> : null}
</div>
</LocalizationProvider>
);
}
const Text = () => <div>You clicked the button!</div>;
export default App;
i have a button component from material UI. What i wanna do is i click a button and it renders to me a new Button on the screen.
That's what i did
const [button, setButton] = useState([]);
function addButton(){
let newObj = {
button: <Button variant="contained"> A Button</Button>,
};
setButton([...button, newObj])
}
And then in the main function return i did
{button.length !== 0 ? (
button.map((item) => {
<div>{item.button}</div>;
})
) : (
<div>Hello</div>
)}
What am i doing wrong?
When you use a multi-line lambda with braces you need a return...
button.map((item) => {
return (<div>{item.button}</div>);
})
Alternatively you can omit the braces...
button.map((item) => (<div>{item.button}</div>))
This answer tries to address the below problem-statement:
But how could i click one button and render as many buttons as i
clicked?
The below snippet provides a demo of how a new button may be rendered when user clicks an existing button. This expands on the below comment
Creating a new button on clicking an existing button may be
accomplished without needing to hold the component in state.
Please note:
It does not store the button elements into the state
instead, it merely stores the attributes (like the button-label /
text) into the state
Code Snippet
const {useState} = React;
const MyButton = ({
btnLabel, btnName, handleClick, isDisabled, ...props
}) => (
<button
class="myBtn"
onClick={handleClick}
name={btnName}
disabled={isDisabled}
>
{btnLabel}
</button>
);
const MagicButtons = () => {
const [btnText, setBtnText] = useState("");
const [disableBtns, setDisableBtns] = useState(false);
const [myButtons, setMyButtons] = useState(["A Button"]);
const handleClick = () => setDisableBtns(true);
return (
<div>
{
disableBtns && (
<div class="myInput">
<input
value={btnText}
onChange={e => setBtnText(e.target.value)}
/>
<button
onClick={() => {
setMyButtons(prev => ([...prev, btnText]));
setBtnText("");
setDisableBtns(false);
}}
>
Add New Button
</button>
</div>
)
}
{
myButtons.map((txt, idx) => (
<MyButton
handleClick={handleClick}
btnName={idx}
isDisabled={disableBtns ? "disabled" : ""}
btnLabel={txt}
/>
))
}
</div>
);
};
ReactDOM.render(
<div>
DEMO
<MagicButtons />
</div>,
document.getElementById("rd")
);
.myBtn { margin: 15px; }
.myInput > input { margin: 15px; }
<div id="rd" />
<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>
You forgot to return the component in the map function
like this
{button.length !== 0 ? (
button.map((item) => {
return (<div>{item.button}</div>);
})
) : (
<div>Hello</div>
)}
the map function with no 'return' keyword must not have the bracket { }
like this
button.map((item) => (<div>{item.button}</div>))
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>