How to toggle a class on the body with React onClick - javascript

I have a button that opens a div that looks like a modal.
On click of this button, I want to add a class on to the body, then on click of a close button, I want to remove this class.
How would you do this in React functional component with useEffect / useState?

You could use this.setState like this in Components
....
handleClick = () => {
this.setState((state) => {
addClass: !state.addClass
})
}
render () {
const { addClass } = this.state
return (
<>
<button onClick={this.handleClick}>Button</button>
<div className={!!addClass && 'yourClassName'}>hello</div>
<>
)
}
And in function like this
function Example() {
const [addClassValue, addClass] = useState(0);
return (
<div>
<button onClick={() => addClass(!addClassValue)}>
Click me
</button>
<div className={addClass ? 'yourClassName'}>hello</div>
</div>
)
}
But you do not need classes because React works with components and you can show your modal like
function Example() {
const [addClassValue, addClass] = useState(false);
return (
<div>
<button onClick={() => addClass(!addClassValue)}>
Click me
</button>
// this Component will be only rendered if addClassValue is true
{addClassValue && <ModalComponent />}
</div>
)
}

Related

How can i do to wrap different component with the same modal

I'm trying to wrap a modal in a different component but only the first showed up.
So I searched for a way to code my modal so I can accept different component open and close the right component instead of open the first component even if i click in another button
{open && <Modal>
<TeamForm />
</Modal>}
{openSavecard && <Modal>
<HikingSaveCards items={cartItems}/>
</Modal>}
// only the first one will display
const Modal = ({children}: Props) => {
const ContentRef = useRef<HTMLDivElement>(null)
const [open,setOpen] = useState(true)
useEffect(() => {
if(!open) return ;
function listener(e:any) {
if (ContentRef.current?.contains(e.target)) return;
setOpen(prev => !prev)
}
window.addEventListener('click', listener)
return () =>{ window.removeEventListener('click', listener)};
}, [open])
return open ? createPortal(
<>
<Background >
<Content ref={ContentRef}>
{children}
</Content>
</Background>
</>, document.querySelector('#portal')!) : null
};
export default Modal

How can I prevent the onmouseleave event from being triggered outside the browser?

There is a basket. When i enter the mouse, the dropdown opens and shows the items in the basket. When i leave the mouse, the dropdown closes. Inside the dropdown, i have a button for each item to remove items from the basket. When we click the button, a modal pops up, and we approve or reject. When this modal pops up, if we move the mouse away from the browser, onMouseEnter event is triggering and and causing the modal to close. How can i prevent this? I want the modal to close only when I click the backdrop or the reject button.
I passed hideDropdown function to child component(BasketItem) and tried to update isModalShow and dropdown state the same handleClick function. The dropdown closed, but the modal didn't pop up. How can i solve this? Can you help?
Basket component:
const Basket = () => {
const [isDropdownShow, setIsDropdownShow] = useState(false);
const { basket } = useAppContext();
const showDropdown = (e) => {
setIsDropdownShow(true);
};
const hideDropdown = (e) => {
setIsDropdownShow(false);
};
return (
<Wrapper
onMouseEnter={showDropdown}
onMouseLeave={hideDropdown}
isDropdownShow={isDropdownShow}
>
Basket
<span className='basket-length'>{basket.length}</span>
{isDropdownShow && <div className='white-border'></div>}
{basket.length === 0 && isDropdownShow && (
<div className='dropdown empty-basket'>Basket is empty</div>
)}
{isDropdownShow && (
<div className='dropdown'>
<ul>
{basket
.map((basketItem, index) => {
return (
<BasketItem
key={index}
basketItem={basketItem}
hideDropdown={hideDropdown}
/>
);
})
.sort(sortBasket)}
</ul>
</div>
)}
</Wrapper>
);
};
export default Basket;
BasketItem Component:
const BasketItem = ({ basketItem, hideDropdown }) => {
const [isModalShow, setIsModalShow] = useState(false);
const handleClick = () => {
hideDropdown();
setIsModalShow(true); //does not trigger
};
return (
<Wrapper>
<img src={basketItem.image} alt='product-item' />
<div>
<p className='basket-description'>{basketItem.description}</p>
<span className='basket-remove' onClick={handleClick}>
Remove
</span>
<Modal
isModalShow={isModalShow}
onClose={() => setIsModalShow(false)}
/>
</div>
</Wrapper>
);
};
export default BasketItem;
Modal:
const Modal = ({ isModalShow, onClose }) => {
if (!isModalShow) return null;
return ReactDom.createPortal(
<>
<Backdrop />
<Wrapper>
<button onClick={onClose}>CloseModal</button>
some content
</Wrapper>
</>,
document.getElementById('overlays')
);
};
export default Modal;

How to render a button in parent component based on children action in react js?

I have a list of movie cards when a user clicks on them, they become selected and the id of each movie card is transferred to an array named "selectedList".
I want to add a "let's go" button below the movie card but conditionally.
I mean when the array is empty the button should not be displayed and when the user clicked on at least a movie the button displays. the array should be checked each time and whenever it becomes equal to zero the button should disappear.
the thing is all the movie cards are the children of this page and I want to render the parent component based on children's behavior.
MY MAIN PAGE:
export default function Index(data) {
const info = data.data.body.result;
const selectedList = [];
return (
<>
<main className={parentstyle.main_container}>
<NavBar />
<div className={style.searchbar_container}>
<SearchBar />
</div>
<div className={style.card_container}>
{info.map((movie, i) => {
return (
<MovieCard
movieName={movie.name}
key={i}
movieId={movie._id}
selected={selectedList}
isSelected={false}
/>
);
})}
</div>
</main>
<button className={style.done}>Let's go!</button>
</>
);
}
**MOVIE CARD COMPONENT:**
export default function Index({ selected, movieName, movieId, visibility }) {
const [isActive, setActive] = useState(false);
const toggleClass = () => {
setActive(!isActive);
};
const pushToSelected = (e) => {
if (selected.includes(e.target.id)) {
selected.splice(selected.indexOf(e.target.id), 1);
console.log(selected);
} else {
selected.push(e.target.id);
console.log(selected);
}
toggleClass();
};
return (
<div>
<img
className={isActive ? style.movie_selected : style.movie}
src={`images/movies/${movieName}.jpg`}
alt={movieName}
id={movieId}
onClick={pushToSelected}
/>
<h3 className={style.title}>{movieName}</h3>
</div>
);
}
You can use conditional rendering for that:
{selectedList.length > 0 && <button className={style.done}>Let's go!</button>}
Plus, you should change your selectedList to a state, and manage the update via the setSelectedList function:
import { useState } from 'react';
export default function Index(data) {
const info = data.data.body.result;
const [selectedList, setSelectedList] = useState([]);
Add the method to the MovieCardĀ as a property:
<MovieCard
movieName={movie.name}
key={i}
movieId={movie._id}
selected={selectedList}
setSelected={setSelectedList}
isSelected={false}
/>;
And update the list in the pushToSelected method:
export default function MovieCard({
selected,
setSelected,
movieName,
movieId,
visibility
}) {
const pushToSelected = (e) => {
if (selected.includes(e.target.id)) {
selected.splice(selected.indexOf(e.target.id), 1);
console.log(selected);
} else {
selected.push(e.target.id);
console.log(selected);
}
setSelected([...selected]);
toggleClass();
};

Rendering different images in a modal based on what button is clicked

I would like to change the content inside a modal based on what button is clicked on.
Depending on the button clicked, a different array of images will be provided to render new <img> tags in the modal.
From my understanding the useState hook should be used here along with map to render each image from the provided image array.
Below is a simplified version of my current code.
const ModalGallery = () => {
...
const [ modalContent, setModalContent ] = useState([]);
const loadImages = imageArray => {
// code that uses map and setModalContent ?
};
return (
<>
{projects.map(project => {
return (
<Button
onClick={() => {
// call a funtion that changes the images rendered in the modal
loadImages(project.images);
}}
>
Display
</Button>
);
})}
<Modal>
{modalContent}
</Modal>
</>
);
};
export default ModalGallery;
You should maintain image urls in modalContent, then on click, change modalContent with image url's of corresponding project, which cause the Modal element to get updated.
const ModalGallery = () => {
...
const [modalContent, setModalContent] = useState([]);
const [showModal, setShowModal] = useState(false)
const modal = () => {
return(
<Model>
{
modalContent.map((img_url) =>{
return(<img src={img_url} key={img_url} />)
})
}
</Model>
)
}
return (
<>
{projects.map(project => {
return (
<Button
onClick={() => {
// call a funtion that changes the images rendered in the modal
setModalContent(project.images);
setShowModal(true)
}}
>
Display
</Button>
);
})}
{showModal ? modal : null}
</>
);
};

How to pass HTML attributes to child component in React?

I have a parent and a child component, child component has a button, which I'd like to disable it after the first click. This answer works for me in child component. However the function executed on click now exists in parent component, how could I pass the attribute down to the child component? I tried the following and it didn't work.
Parent:
const Home = () => {
let btnRef = useRef();
const handleBtnClick = () => {
if (btnRef.current) {
btnRef.current.setAttribute("disabled", "disabled");
}
}
return (
<>
<Card btnRef={btnRef} handleBtnClick={handleBtnClick} />
</>
)
}
Child:
const Card = ({btnRef, handleBtnClick}) => {
return (
<div>
<button ref={btnRef} onClick={handleBtnClick}>Click me</button>
</div>
)
}
In general, refs should be used only as a last resort in React. React is declarative by nature, so instead of the parent "making" the child disabled (which is what you are doing with the ref) it should just "say" that the child should be disabled (example below):
const Home = () => {
const [isButtonDisabled, setIsButtonDisabled] = useState(false)
const handleButtonClick = () => {
setIsButtonDisabled(true)
}
return (
<>
<Card isDisabled={isButtonDisabled} onButtonClick={handleButtonClick} />
</>
)
}
const Card = ({isDisabled, onButtonClick}) => {
return (
<div>
<button disabled={isDisabled} onClick={onButtonClick}>Click me</button>
</div>
)
}
Actually it works if you fix the typo in prop of Card component. Just rename hadnlBtnClick to handleBtnClick
You don't need to mention each prop/attribute by name as you can use javascript Object Destructuring here.
const Home = () => {
const [isButtonDisabled, setIsButtonDisabled] = useState(false)
const handleButtonClick = () => {
setIsButtonDisabled(true)
}
return (
<>
<Card isDisabled={isButtonDisabled} onButtonClick={handleButtonClick} />
</>
)
}
const Card = (props) => {
return (
<div>
<button {...props}>Click me</button>
</div>
)
}
You can also select a few props and use them differently in the child components. for example, see the text prop below.
const Home = () => {
const [isButtonDisabled, setIsButtonDisabled] = useState(false)
const handleButtonClick = () => {
setIsButtonDisabled(true)
}
return (
<>
<Card text="I'm a Card" isDisabled={isButtonDisabled} onButtonClick={handleButtonClick} />
</>
)
}
const Card = ({text, ...restProps}) => {
return (
<div>
<button {...restProps}>{text}</button>
</div>
)
}

Categories

Resources