How to open dynamic modal with react js - reactjs

I am trying to convert the HTML/Javascript modal to React js.
In Reactjs, I just want to open the modal whenever the user clicks the View Project button.
I have created a parent component (Portfolio Screen) and a child component (Portfolio Modal). The data I have given to the child component is working fine but the modal opens the first time only and then does not open. Another problem is that the data does not load even when the modal is opened the first time.
Codesandbox link is here.
https://codesandbox.io/s/reverent-leftpad-lh7dl?file=/src/App.js&resolutionWidth=683&resolutionHeight=675
I have also shared the React code below.
For HTML/JavaScript code, here is the question I have asked before.
How to populate data in a modal Popup using react js. Maybe with hooks
Parent Component
import React, { useState } from 'react';
import '../assets/css/portfolio.scss';
import PortfolioModal from '../components/PortfolioModal';
import portfolioItems from '../data/portfolio';
const PortfolioScreen = () => {
const [portfolio, setportfolio] = useState({ data: null, show: false });
const Item = (portfolioItem) => {
setportfolio({
data: portfolioItem,
show: true,
});
};
return (
<>
<section className='portfolio-section sec-padding'>
<div className='container'>
<div className='row'>
<div className='section-title'>
<h2>Recent Work</h2>
</div>
</div>
<div className='row'>
{portfolioItems.map((portfolioItem) => (
<div className='portfolio-item' key={portfolioItem._id}>
<div className='portfolio-item-thumbnail'>
<img src={portfolioItem.image} alt='portfolio item thumb' />
<h3 className='portfolio-item-title'>
{portfolioItem.title}
</h3>
<button
onClick={() => Item(portfolioItem)}
type='button'
className='btn view-project-btn'>
View Project
</button>
</div>
</div>
))}
<PortfolioModal portfolioData={portfolio} show={portfolio.show} />
</div>
</div>
</section>
</>
);
};
export default PortfolioScreen;
Child Component
import React, { useState, useEffect } from 'react';
import { NavLink } from 'react-router-dom';
const PortfolioModal = ({ portfolioData, show }) => {
const portfolioItem = portfolioData;
const [openModal, setopenModal] = useState({ showState: false });
useEffect(() => {
setopenModal({
showState: show,
});
}, [show]);
return (
<>
<div
className={`portfolio-popup ${
openModal.showState === true ? 'open' : ''
}`}>
<div className='pp-inner'>
<div className='pp-content'>
<div className='pp-header'>
<button
className='btn pp-close'
onClick={() =>
setopenModal({
showState: false,
})
}>
<i className='fas fa-times pp-close'></i>
</button>
<div className='pp-thumbnail'>
<img src={portfolioItem.image} alt={`${portfolioItem.title}`} />
</div>
<h3 className='portfolio-item-title'>{portfolioItem.title}</h3>
</div>
<div className='pp-body'>
<div className='portfolio-item-details'>
<div className='description'>
<p>{portfolioItem.description}</p>
</div>
<div className='general-info'>
<ul>
<li>
Created - <span>{portfolioItem.creatDate}</span>
</li>
<li>
Technology Used -
<span>{portfolioItem.technologyUsed}</span>
</li>
<li>
Role - <span>{portfolioItem.Role}</span>
</li>
<li>
View Live -
<span>
<NavLink to='#' target='_blank'>
{portfolioItem.domain}
</NavLink>
</span>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</>
);
};
export default PortfolioModal;

You don't have to use one useState hook to hold all your states. You can and I think you should break them up. In the PortfolioScreen component
const [data, setData] = useState(null);
const [show, setShow] = useState(false);
I changed the function Item that is used to set the active portfolio item to toggleItem and changed it's implementation
const toggleItem = (portfolioItem) => {
setData(portfolioItem);
setVisible(portfolioItem !== null);
};
You should use conditional rendering on the PortfolioModal, so you won't need to pass a show prop to it, and you'll pass a closeModal prop to close the PortfolioModal when clicked
{visible === true && data !== null && (
<PortfolioModal
data={data}
closeModal={() => toggleItem()} // Pass nothing here so the default value will be null and the modal reset
/>
)}
Then in the PortfolioModal component, you expect two props, data and a closeModal function
const PortfolioModal = ({ data, closeModal }) => {
And the close button can be like
<button className="btn pp-close" onClick={closeModal}>
...

Related

Add element in array by button click

I have product cards that are rendered based on a json file.
By clicking on the "Add to Cart" button, the element should be added to the array сartList, but this does not happen.
I also tried to forward the function to the component itself, but it didn’t work out too well for me.
Shop.jsx:
import React, { useState } from 'react';
import './Instruments.css';
import Cart from '../components/Cart'
import Product from '../components/Product'
import cart from '../img/cart.png';
import data from "../data/data.json";
unction Shop() {
const [value, setValue] = useState('');
const [currentData, setCurrentData] = useState(data);
const [cartList, setCartList] = useState([]);
return (
<div className='shop'>
<div className='container'>
<div className='shop__main-products'>
{
currentData.filter((el) => {
return value.toLowerCase() === '' ? el : el.title.toLowerCase().includes(value.toLowerCase())
}).map((el, index) => {
return (
<Product img={el.img} title={el.title} price={el.price} key={el.id} onClick={() => setCartList([...cartList, el])}/>
)
})
}
</div>
</div>
<Cart active={modalActive} setActive={modalSetActive}/>
</div>
</div>
);
}
export default Shop;
Product.jsx:
import React, { useState } from 'react';
import './Product.css';
function Product({img, title, price, id, type}) {
return (
<div className='product' key={id} type={type}>
<div className='buy__top'>
<div className='product__top-image-background'>
<img className='product__top-image' src={img}></img>
</div>
<h3 className='product__top-title'>{title}</h3>
</div>
<div className='product__buy'>
<h3 className='product__buy-price'>{price} грн</h3>
<button className='product__buy-button'>В корзину</button>
</div>
</div>
)
}
export default Product;
It looks like the issue is with how you're passing the onClick function to the Product component. The onClick prop should be passed to the "Add to Cart" button, not the Product component itself. You should change the following line:
<Product img={el.img} title={el.title} price={el.price} key={el.id} addToCart={() => setCartList([...cartList, el])}/>
And in the Product component, you should add the onClick prop to the "Add to Cart" button:
<button className='product__buy-button' onClick={addToCart}>В корзину</button>
This way, when the button is clicked, it will call the addToCart function and add the element to the cartList array.
You are not adding the onClick function to the props of the Product component pass it down the pipe and itll work.
function Product({img, title, price, id, type, onClick}) {
return (
<div className='product' key={id} type={type}>
<div className='buy__top'>
<div className='product__top-image-background'>
<img className='product__top-image' src={img}></img>
</div>
<h3 className='product__top-title'>{title}</h3>
</div>
<div className='product__buy'>
<h3 className='product__buy-price'>{price} грн</h3>
<button className='product__buy-button' onClick={onClick}>В корзину</button>
</div>
</div>
)
}

Unable to load pdf in react

I'm trying to load a pdf file for the user in another tab once they click a button but it's not working and I'm not sure how to make it work. Could I have some help in doing this?
I defined a function PdfViewer() and I call it when a button is clicked using onClick(), but once the button is clicked I get this error:
Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
Here's my code:
import "../styles/ProjectDetails.css";
import React, { useState } from 'react'
import { Document, Page } from 'react-pdf/dist/esm/entry.webpack'
function PdfViewer() {
const [numPage, setNumPages] = useState(null);
const [pageNumber, setPageNumber] = useState(1);
function onDocumentLoadSuccess({numPages}) {
setNumPages(numPage);
setPageNumber(1);
}
return (
<div>
<header>
<Document file="../pdfs/Mini_Case_Study_15.pdf" onLoadSuccess={onDocumentLoadSuccess}>
<Page height="600" pageNumber={pageNumber}></Page>
</Document>
</header>
</div>
)
}
const ProjectDetails = ({ project }) => {
return (
<div className="card-grid">
<div className="card">
<div className="card-header card-image">
<img src="https://c4.wallpaperflare.com/wallpaper/672/357/220/road-background-color-hd-wallpaper-thumb.jpg"/>
</div>
<div className="card-title"><strong>{project.sdg}</strong></div>
<div className="card-body">
<strong>Goal:</strong> {project.goal}
</div>
<div className="card-themes">
<strong>Themes:</strong> {project.theme.map((theme)=>{return theme + ', '})}
</div>
<div className="card-assignment">
<strong>Assignment Type:</strong> {project.assignment_type}
</div>
<div className="card-footer">
<button className="btn">Details</button>
{project.assignment_type === 'Mini Case Studies' &&
<>
<button className="btn btn-outline">Download</button>
{/* <button onClick={PdfViewer} className="btn">Preview</button> */}
</>
}
</div>
</div>
</div>
)
}
export default ProjectDetails
How do I make it so that once the user clicks the button, it takes them to another page with the pdf file shown?
You could try this approach here, inserting the Preview as a Component.
const ProjectDetails = ({ project }) => {
const [preview, setPreview] = useState(false)
const onClickToPreviewPDF = () => {
setPreview(preview ? false : true);
}
return (
<>
<div className="card-grid">
<div className="card">
<div className="card-header card-image">
<img src="https://c4.wallpaperflare.com/wallpaper/672/357/220/road-background-color-hd-wallpaper-thumb.jpg"/>
</div>
<div className="card-title"><strong>{project.sdg}</strong></div>
<div className="card-body">
<strong>Goal:</strong> {project.goal}
</div>
<div className="card-themes">
<strong>Themes:</strong> {project.theme.map((theme)=>{return theme + ', '})}
</div>
<div className="card-assignment">
<strong>Assignment Type:</strong> {project.assignment_type}
</div>
<div className="card-footer">
<button className="btn">Details</button>
{project.assignment_type === 'Mini Case Studies' &&
<>
<button className="btn btn-outline">Download</button>
<button onClick={onClickToPreviewPDF} className="btn">Preview</button>
</>
}
</div>
</div>
</div>
{preview && <PdfViewer />}
</>
)
}

button onclick refresh page instead of set state to false

I am developing an ecommerce application and I am working on the login.
I want the login to be a pop up box such that when the user click on login button the dialog box will appear and when the user click on the x button the dialog box disappears.
Other things work well except the close(x) button.
When it is clicked it refreshes the page instead of setting setOpenLoginModal to false.
I created a reusable modal then imported it to LoginModal.js which is passed to App.js.
Modal.js
const Modal = ({open, children, handleSubmit}) => {
if(!open) return null
return ReactDom.createPortal(
<>
<form action="" onSubmit={handleSubmit}>
<div style={OVERLAY_STYLE}>
<div style={MODAL_STYLES}>
{children}
</div>
</div>
</form>
</>,
document.getElementById('portal')
)
}
export default Modal
LoginModal.js
import Modal from './Modal'
const LoginModal = ({openLoginModal, setOpenLoginModal}) => {
return (
<div>
<Modal open={openLoginModal} handleSubmit={handleSubmit}>
<div className="d-flex justify-content-center">
<div>
<h4 className="head">eTranzact eCommerce</h4>
<h6 className="sub-heading">Create an account to list your own product</h6>
</div>
<div>
<h2 style={{position: 'absolute', right: '2em'}}>
<button className="close" type="button" onClick={() => setOpenLoginModal(false)}>x</button>
</h2>
</div>
</div>
<hr />
</div>
)
}
export default LoginModal
App.js
import LoginModal from '../Components/LoginModal';
const App = () => {
const [openLoginModal, setOpenLoginModal] = useState(false)
return (
<div>
{
openLoginModal && (
<LoginModal openLoginModal={openLoginModal} setOpenRegisterModal={ () => setOpenRegisterModal(false) } />
)
}
<div className="right-navs">
<button onClick={() => setOpenLoginModal(true)} className="btn btn-primary">
LOGIN
</button>
</div>
</div>
)
}
export default App
This issue is probably with your form, I don't think you are preventing the default. Even though you are saying action="" it will still try and process it like a get action which would be causing it to refresh the page.
const Modal = ({ open, children, handleSubmit }) => {
if (!open) return null
const submit = (e) => {
e.preventDefault()
handleSubmit()
}
return ReactDom.createPortal(
<>
<form action="" onSubmit={submit}>
<div style={OVERLAY_STYLE}>
<div style={MODAL_STYLES}>{children}</div>
</div>
</form>
</>,
document.getElementById('portal')
)
}
export default Modal
I can see you're passing the wrong setState handler to the child component. You have to change setOpenRegisterModal to setOpenLoginModal in your App.js file

React menu toggle typeError (gsap, object is not extensible)

I having some hard time to figure out some issues on my Gatsby application (i'm new to Gatssby. Basically I'm trying to implement open, close menu functionality using GSAP. The issue I'm facing is in my if else conditional statement then I'm toggling between the open close menu states.
Strange thing is on my if statement gsap works just fine, but on else condition, then I add gsap function in it I get error "can't define property "_gsap": Object is not extensible". Because I'm new to React and GSAP, I can't figure out what I'm doing wrong? There from this error coming from and how could I solve it?
Here is my code:
menu.js
import React, { useEffect, useRef } from "react";
import { Link } from "gatsby";
import Navbar from "./Navbar";
import gsap from "gsap";
import {
Github,
LinkedIn,
Instagram,
Facebook,
Twitter,
Close,
} from "../assets/svg/social-icons";
const Menu = ({ menuState, setMenuState }) => {
// Create varibles of our dom nodes
let menu = useRef(null);
let revealMenu = useRef(null);
let revealMenuBackground = useRef(null);
useEffect(() => {
if (menuState) {
console.log("CLOSE");
gsap.to([revealMenu, revealMenuBackground], {
duration: 0.8,
height: 0,
ease: "power3.inOut",
stagger: { amount: 0.07 },
});
gsap.to(menu, {
duration: 1,
css: { display: "none" },
});
} else if (!menuState) {
console.log("OPEN");
// ERROR COMES FROM HERE
gsap.to([revealMenu, revealMenuBackground], {});
}
});
return (
<>
{menuState && (
<div ref={(el) => (menu = el)} className="hamburger-menu">
<div
ref={(el) => (revealMenuBackground = el)}
className="menu-secondary-background-color"
></div>
<div ref={(el) => (revealMenu = el)} className="menu-layer">
<div className="wrapper">
<div className="container">
<div onClick={() => setMenuState(!menuState)} className="close">
<Close />
</div>
</div>
<div className="menu-city-background"></div>
<span className="menu-text">Menu</span>
<div className="menu-links">
<Navbar />
</div>
<div className="menu-social-icons">
<IconList />
</div>
<button className="btn menu-resume">Resume</button>
</div>
</div>
</div>
)}
</>
);
};
const IconList = () => {
return (
<li>
<a className="icon social-icon" href="#">
<Github />
</a>
<a className="icon social-icon" href="#">
<Instagram />
</a>
<a className="icon social-icon" href="#">
<LinkedIn />
</a>
<a className="icon social-icon" href="#">
<Facebook />
</a>
<a className="icon social-icon" href="#">
<Twitter />
</a>
</li>
);
};
export default Menu;
When using useRef, you can access your element (what gsap needs) with reference.current.
In your case, you should add .current to menu, revealMenu and revealMenuBackground, otherwise gsap won't understand what you are passing.
Example:
gsap.to([revealMenu.current, revealMenuBackground.current], {});
Also, on your return you should define your references as for example ref={menu} so the reference of that element gets attached to the current reference defined by useRef.
Last but not least, in the useEffect you should wrap everything within a condition to check if that ref contains a value, as such:
useEffect(() => {
if(menu?.current) {
// Do your animations here
}
}
This is because of React components lifecycle, where:
The reference gets instanciated with null
useEffect runs and the ref will be null
The component renders the jsx (this is where the ref gets attached to its element)
Now useEffect will contain the references

Reset component into modal

I have a component that is inserted in a modal and that includes a CheckListBox. When the modal starts each time, the component is not reset. How can I do? How Force reset? I use reactjs with hooks.
How can I trigger a reset event every time the modal opens?
Thanks a lot.
const CheckList = ({title, api, color, onChange }) => {
const [items, setItems] = useState([]);
let listCheck = [];
useEffect(() => {
axiosApi.get( `${api}`).then((res)=>{
setItems(res.data);
})
}, [])
function handleClick(ev, item) {
if (ev.target.checked) {
listCheck.push(item)
onChange(listCheck);
}
else
{
listCheck = listCheck.filter(riga => {
return (riga.id !== item.id)});
onChange(listCheck);
}
}
return (
<>
<div class="card rd-card-list">
<div class="card-header">
{title}
</div>
<div class="card-content rd-card-content">
<div class="content rd-scroll">
<ul class="rd-ul">
{ items.map( (item) =>
<li class="rd-li" key={item.id}>
<label class="checkbox">
{item.description}
</label>
<input type="checkbox" onClick={(ev) => handleClick(ev, item)}/>
</li>
)
}
</ul>
</div>
</div>
</div>
</>
);
}
export default CheckList;
in my modal.js
<CheckList title="mytititle" api="/api/users" onChange={(itx) => {
formik.setFieldValue('users', itx)
} }/>
The easiest way is to not render the modal until it's open:
<div>
{modalOpen &&
<Modal open={modalOpen}>
<CheckList title="mytititle" api="/api/users" onChange={(itx) => {
formik.setFieldValue('users', itx)
} }/>
</Modal>
}
</div>
So whenever you close the modal, it will be removed from DOM, along with any data that this component had.
React life cycle events can be used to perform operation before a component can be rendered. 'constructor()' or 'componentDidMount()' can be used in class components to reset the data or any other operation before rendering the component.
Since you are using function component, you can use React hooks to mimic the life cycle events using 'useEffect()'.

Resources