How to open dynamic modal with react js

Viewed 849

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;
1 Answers

Edit vibrant-oskar-y4bfx

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