React.js - Disable background scroll when a modal is showing - reactjs

I am trying to disable the background body scroll when a modal pop up window is open. I created the Modal using React portal. The only way I can think of doing this is by giving the body an overflow-y property when the Modal is open, but am unsure how to execute this..
my Modal.js looks like this
export default function Modal({open, onClose, image, title, description}){
if (!open) return null
return ReactDom.createPortal(
<>
<div id="overlay" />
<div id="modal" >
<button title={title} onClick={onClose}>close</button>
<h3>{title}</h3>
<img src={image} className="modal-img"/>
{description}
</div>
</>,
document.getElementById('portal')
)
}
and the component where I am 'rendering' the model
const ProjectCardUI = (props)=>{
const[isOpen, setIsOpen] = useState(false)
function openModal() {
setIsOpen(true)
// can I set the styling for '.body' or 'html' here?
}
return(
<div className="card text-center container-fluid d-flex">
<a className="modal-click" onClick ={openModal}>this is a link</a>
<Modal
open={isOpen}
onClose={() => setIsOpen(false)}
title={props.cardName}
image={props.imgsrc}
description={props.cardDescription}>
</Modal>
</div>
)
};
any help would be great.

You can directly manipulate the body style via the document object.
For example:
import React from "react";
import ReactDOM from "react-dom";
export default function Test() {
const setHidden = () => {
console.log(document.body.style.overflow);
if (document.body.style.overflow !== "hidden") {
document.body.style.overflow = "hidden";
} else {
document.body.style.overflow = "scroll";
}
};
return (
<div>
<button onClick={setHidden}>Click me! </button>
<h1>1</h1>
<h1>2</h1>
<h1>3</h1>
<h1>4</h1>
<h1>5</h1>
<h1>6</h1>
<h1>7</h1>
<h1>8</h1>
<h1>9</h1>
<h1>10</h1>
<h1>11</h1>
<h1>12</h1>
<h1>13</h1>
<h1>14</h1>
<h1>15</h1>
<h1>16</h1>
</div>
);
}
ReactDOM.render(<Test />, document.getElementById("container"));

Maybe something like this:
const ProjectCardUI = (props) => {
const [isOpen, setIsOpen] = useState(false);
function openModal() {
setIsOpen(true);
// can I set the styling for '.body' or 'html' here?
}
useEffect(() => {
const body = document.querySelector('body');
body.style.overflow = isOpen ? 'hidden' : 'auto';
}, [isOpen])
return (
<div className="card text-center container-fluid d-flex">
<a className="modal-click" onClick={openModal}>
this is a link
</a>
<Modal
open={isOpen}
onClose={() => setIsOpen(false)}
title={props.cardName}
image={props.imgsrc}
description={props.cardDescription}
></Modal>
</div>
);
};

Related

How can I close my modal by clicking the close button in reactjs?

How can I close my modal by clicking the close button in reactjs ? My closeModal function is not working. Help me please !!! When I click the close button, the modal does not close.
When, I click on open the modal button then it is working, but when i click on close button then close button is not working. I am using the hooks in my project. Below, I have mentioned my code :
Gallery.jsx
import React, { useState } from 'react'
import "./gallery.css"
import { FaChevronLeft, FaChevronRight } from "react-icons/fa";
const Gallery = ({ pictures, source1, source2, source3, source4, source5, source6, title }) => {
const [clickedImg, setClickedImg] = useState(null);
const [currentPictureIndex, setCurrentPictureIndex] = useState(0);
const handleClick = (e, pictures, title ) => {
setClickedImg({e, pictures, title })
console.log("showmodal state before any click")
};
const closeModal = (e) => {
try {
if (e.target.classList.contains("dismiss")) {
setClickedImg(null);
console.log(e.target.classList.contains("dismiss"))
}
} catch (error) {
console.log(error);
}
};
const handleNextClick = () => {
const nextIndex = currentPictureIndex === pictures.length - 1 ? 0 : currentPictureIndex + 1;
setCurrentPictureIndex(nextIndex);
};
const handlePrevClick = () => {
const prevIndex = currentPictureIndex === 0 ? pictures.length - 1 : currentPictureIndex - 1;
setCurrentPictureIndex(prevIndex);
};
return (
<div className="gallery" onClick={handleClick}>
<div className="galleryBox1">
<img src={source1} alt={title} className="galleryCard"/>
</div>
<div className="galleryBox2">
<div className="galleryBox3">
<div className="galleryBoxa">
<img src={source2} alt={title} className="galleryCardTwo"/>
</div>
<div className="galleryBoxb">
<img src={source3} alt={title} className="galleryCardTwo"/>
</div>
</div>
<div className="galleryBox5">
<div className="galleryBoxc">
<img src={source4} alt={title} className="galleryCardTwo"/>
</div>
<div className="galleryBoxd">
<img src={source5} alt={title} className="galleryCardTwo"/>
</div>
</div>
</div>
{
clickedImg && (
<div className="galleryModal dismiss" onClick={closeModal}>
<img
src={pictures[currentPictureIndex]}
alt={title}
className="galleryModalImg"
/>
<button className="galleryModalButton dismiss" onClick={closeModal}>
Fermer
</button>
{pictures.length !== 1 && (
<div className="galleryModalArrows" onClick={handleNextClick}>
<FaChevronRight className="galleryModalNext" />
</div>
)}
{pictures.length !== 1 && (
<div className="galleryModalArrows" onClick={handlePrevClick}>
<FaChevronLeft className="galleryModalPrev" />
</div>
)}
</div>
)}
</div>
)
}
export default Gallery
How can I close my modal by clicking the close button in reactjs ?
I have figured out the reason. As you have added onClick in the parent div (className="gallery"), That's why, whenever you are clicking on any button in the div it is triggering a click on the main div's handleClick method.
If you console on the handleClick and closeModal function. You will see that if you click on the close button you will see the console that is written in the handleClick() function.
So I tried to move the onClick={handleClick} from that parent div. And put it in a children div. You can put it on className="galleryBox1" this div or any other div. Then it will work fine.

SlateJS React trying to get a textfield to work inside an Element

I have a Slate element that looks like this:
import React, {useState} from 'react'
import "./App.css"
export default function Accordion(props) {
const [closed, setClosed] = useState(false) //todo eventually fetch starting position from savefile
const [heightClass, setHeightClass] = useState("")
const handleToggle = () => {
if(closed === false){
setHeightClass("h-0")
}
else{
setHeightClass("")
}
setClosed(!closed)
}
return (
<>
<div {...props.attributes}>
{/* title and button */}
<div className='flex justify-between '>
<div className='font-semibold'>
<DefaultElement {...props}/> //the title of the accordion
</div>
<div
className={`px-2 cursor-pointer font-bold font-mono select-none ${closed ? "rotate-180" : ""}`}
onClick={() => {
handleToggle()
}}>
V
</div>
</div>
{/* ${closed ? "h-0" : ""} */}
<div className={`rounded border-l px-2 overflow-hidden accordionTransition `}
style={{height: closed ? "0px" : ""}}>
{props.children}
</div>
</div>
</>
)
}
const DefaultElement = props => {
console.log(props)
return <p {...props.attributes}>
{props.children}
</p>
}
Which is used by the Editor in App.js:
const App = () => {
const [editor] = useState(() => withReact(createEditor()))
// Define a rendering function based on the element passed to `props`. We use
// `useCallback` here to memoize the function for subsequent renders.
const renderElement = useCallback(props => {
switch (props.element.type) {
case 'accordion':
return <Accordion {...props} />
default:
return <DefaultElement {...props} />
}
}, [])
return (
<div className='p-5'>
<Slate editor={editor} value={initialValue}>
<Editable
// Pass in the `renderElement` function.
renderElement={renderElement}
/>
</Slate>
</div>
)
}
const DefaultElement = props => {
return <span {...props.attributes}>
{props.children}
</span>
}
export default App;
I'm trying to get the Accordion title to be properly editable and work as Slate intended. Just a a simple textfield. I can't figure out the correct syntax and how i'm supposed to do this.
When i do this, the title and the accordion content (props.children) are the same. I don't understand why.
If i remove the <DefaultElement {...props}/> and just write some text, it throws me this error when i edit it: Uncaught Error: Cannot resolve a Slate point from DOM point: [object Text],4

How to toggle Modal and pass/change data

I am new to reacting and having trouble understanding how to pass data meta into each modal when an image is clicked and update the modal with the clicked data info. Following in my bare minimum code for sake of example
app.js
<div className="movie">
<Modal >hello world/*making sure the static text is passed into as children prop*/</Modal>
{movies.length > 0 &&
movies.map((data) => {
return <Library key={data.id} {...data} searchTerm={searchTerm} />;
})}
</div>
modal.jsx
export default function Index({children}) {
const [isOpen, setIsOpen] = useState(true)
return (
isOpen && (
<div className='modalContainer'>
<div className="modal">
<div className="close">
<button onClick={()=>{
setIsOpen(false)
}}>close</button>
</div>
{children}
</div>
</div>
)
)
}
Library.jsx
import "./Library.scss";
import {Link} from "react-router-dom";
const IMG_API = "http://image.tmdb.org/t/p/w1280";
const Library = ({
title,
poster_path,
release_date,
}) => {
return (
<div>
<Link to="/modal">
<img src={IMG_API + poster_path} alt={title} />
</Link>
<div className="meta">
<h5>{title}</h5>
<p>{release_date.slice(0, 4)}</p>
</div>
</div>
);
};
export default Library;
You should declare the isOpen state in the upper-level component so you can actually open the modal on some kind of event.
Also, you should declare a props where to pass the actual text to the Modal component:
const [isOpen, setIsOpen] = useState(false)
const handleOpen = () => setIsOpen(true);
<div className='movie'>
<Modal isOpen={isOpen} setIsOpen={setIsOpen} text='Hello, world' />
{movies.length > 0 &&
movies.map((data) => {
return <Library key={data.id} {...data} searchTerm={searchTerm} />;
})}
<button type='button' onClick={() => handleOpen()}>Open modal</button>
</div>;
You should then change your Modal declaration as:
export default function Index({ isOpen, setIsOpen, text }) {
return (
isOpen && (
<div className='modalContainer'>
<div className="modal">
<div className="close">
<button onClick={()=>{
setIsOpen(false)
}}>close</button>
</div>
{text}
</div>
</div>
)
)
}

Trying to change React Style attribute by changing state hook

Transform is not updating the style component below. I have console logged the state to ensure that it is changing and it was, just not the style component (it kept the color red).
import React, {useState} from 'react';
const Carousel = () => {
let [selectedIndex, setSelectedIndex] = useState(0)
const [cellCount,] = useState(9);
let [transform, setTransform] = useState({color: 'red'});
const prevButton = () => {
setSelectedIndex(selectedIndex-1);
setTransform({color: 'green !important'})
console.log(selectedIndex)
console.log(transform)
};
const nextButton = () => {
setSelectedIndex(selectedIndex+1);
setTransform({color: 'red !important'})
console.log(transform)
}
return (
<>
<div className="scene">
<div style={transform} className="carousel">
<div className="carousel__cell">1</div>
<div className="carousel__cell">2</div>
<div className="carousel__cell">3</div>
<div className="carousel__cell">4</div>
<div className="carousel__cell">5</div>
<div className="carousel__cell">6</div>
<div className="carousel__cell">7</div>
<div className="carousel__cell">8</div>
<div className="carousel__cell">9</div>
</div>
</div>
<button onClick={prevButton}>
Previous
</button>
<button onClick={nextButton}>
Next
</button>
</>
)
}
React inline style doesn't know the !important property. There is usually always a way to avoid using it, and it is better to do so.
If you just don't avoid using !important, it will work here.
If you have to set it, this would work:
import React, { useState } from 'react';
const Carousel = () => {
let [selectedIndex, setSelectedIndex] = useState(0);
let [transform, setTransform] = useState({ color: 'red' });
const prevButton = () => {
setSelectedIndex(selectedIndex - 1);
setTransform({ ...{ color: 'green' } });
// rotateCarousel();
console.log(selectedIndex);
console.log(transform);
};
const nextButton = () => {
setSelectedIndex(selectedIndex + 1);
setTransform({ ...{ color: 'red' } });
console.log(transform);
// rotateCarousel();
};
return (
<>
<div className="scene">
<div
ref={el => {
if (el) {
el.style.setProperty('color', transform.color, 'important');
}
}}
className="carousel"
>
<div className="carousel__cell">1</div>
{transform.color}
<div className="carousel__cell">2</div>
<div className="carousel__cell">3</div>
<div className="carousel__cell">4</div>
<div className="carousel__cell">5</div>
<div className="carousel__cell">6</div>
<div className="carousel__cell">7</div>
<div className="carousel__cell">8</div>
<div className="carousel__cell">9</div>
</div>
</div>
<button onClick={prevButton}>Previous</button>
<button onClick={nextButton}>Next</button>
{transform.color}
</>
);
};
export default Carousel;

adding and removing a classList in react js

I am using functional component in react js , in the below code
the class right-panel-active is not being added / is undefined. Someone help to enable the class be added when the button is toggled
import React from 'react';
import './style.css';
import {
Modal,
DropdownMenu
} from '../MaterialUI';
/**
* #author
* #function Header
**/
const Header = (props) => {
const container = () => {
document.getElementById('container');
}
const signUpButton = () => {
container.classList.add('right-panel-active');
};
const signInButton = () => {
container.classList.remove('right-panel-active');
};
return (
<div className="header">
<div className="container" id="container">
<button className="ghost" id="signIn" onClick={signInButton} >Sign In</button>
</div>
<div className="overlay-panel overlay-right">
<p>Enter your personal details and start journey with us</p>
<button className="ghost" id="signUp" onClick={signUpButton} >Sign Up</button>
</div>
</div>
)
}
export default Header
You are not utilising any of React's functionality.
Read about state management in React
and Event Handlers in React
const Header = (props) => {
const [isContainerActive, setIsContainerActive] = React.useState(false);
const signUpButton = () => {
setIsContainerActive(false);
};
const signInButton = () => {
setIsContainerActive(true);
};
return (
<div className="header">
<div id="container" className={`container${isContainerActive ? " right-panel-active" : ""}`}>
<button className="ghost" id="signIn" onClick={signInButton}>Sign In</button>
</div>
<div className="overlay-panel overlay-right">
<p>Enter your personal details and start journey with us</p>
<button className="ghost" id="signUp" onClick={signUpButton}>Sign Up</button>
</div>
</div>
);
}
ReactDOM.render(<Header />, document.getElementById("root"));
.header {height: 120px;}
.container {float:left;}
.overlay-right {display: none;background: red;float:right;height:100%;}
.right-panel-active ~ .overlay-right {display: inline-block;}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
PS: I also recommend https://www.npmjs.com/package/classnames , or the cx library for className management.
I think the best solution for this is to making a state and handle the style by a ternarian
function Welcome(props) {
const {name} = props;
return (
<h1 style={name === 'Sara' ? right-panel-active : right-panel-inactive}>Hello, {name}</h1>
)}
function App() {
return (
<div>
<Welcome name="Sara" />
</div>
);
}
This is the React way of doing,
You have to keep a local state (useState) and track the button click and based on the state change update the class to the container. You shouldn't directly change the DOM.
Click on the button to see the CSS is adding or not.
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const [isSignUp, setSignUp] = useState(true);
return (
<div id="container" className={isSignUp ? "right-panel-active" : ""}>
<button onClick={() => setSignUp(false)}>SignIn</button>
<button onClick={() => setSignUp(true)}>SignUp</button>
//for testing the change
{isSignUp ? "right-panel-active added" : "right-panel-active removed"}
</div>
);
}
Sample working code - https://codesandbox.io/s/fragrant-http-qo6du?file=/src/App.js
No need to add click event with vanilla js, you could add an React onClick event. You forgot to return the container.
// if you use curly braces you must return the value
const container = () => {
return document.getElementById('container');
};
// or skip the curly braces
const container = () =>
document.getElementById('container');

Resources