javascriptreactjsreact-hooksreact-statesimplemodal

Is there anyway to close this modal that I have written for my React APP?


I am new to software development and I am trying to create a modal UI element using ReactJS and fetching data from Pokeapi.co. I have gotten all the data to properly load using State and Axios. However, when I tried to create the modal that loads some data from PokeAPI, the modal just sits on the UI. I have tried different ways to return UI back to no Modal but have had no luck. Here is the Code that I am working with currently as well as the link to the GitHub repo. text]

import React from 'react'
import './Pokemon.css'

export const PokemonDetails = ({data, setPokeModal}) => {
 
>!  console.log(data)
>! const checkClose = () => {
>! setPokeModal = false 
>! console.log(setPokeModal)
  }
  return (
    <>
      {
        (!data) ? '': (
          <>
            
>! <div className="BackShadow">
>!             <div className='Modal'>
>!             <div className='close-btn'><button onClick={()=>checkClose()}>X</button></div>
            <div>
            <h1>{data.name}</h1>
            <img src={`https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/home/${data.id}.png`} alt=''/>
          </div>
          <div className='TypesContainer'>
            {
              data.types.map(pokemon=>{
                return (
                  <>
                    <h2>
                    {pokemon.type.name}
                    </h2>
                  </>
                )
              })
            }
       </div>
>!         </div>
>!         </div>
        </>
        )

      }
      
    </>
  )
}

The code snippet above is the modal element that handles the data that I want to render when the modal loads. The code currently has the modal comment out.

The code snippet below handles the CSS that create the modal and the styling for the content within the modal.

.BackShadow {
    width: 100%;
    height: 100vh;
    position: fixed;
    top: 0;
    left: 0px;
    background-color: rgba(0, 0, 0, 0.7);
    display: flex;
    justify-content: center;
    align-items: center;
}

.BackShadow .Modal {
    width: 800px;
    height: 800px;
    background-color: rgb(188, 247, 247);
    position: relative;
}

.close-btn button{
    width: 25px;
    height: 25px;
    border-radius: 5px;
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
    position: absolute;
    top: -15px;
    right: -15px;


}


.PokeDetails {
    width: 13%;
    text-align: center;
    color: black;
    position: fixed;
    top: 100px;
    right: 200px;
}

.PokeDetails img {
    margin: 2rem;
    height: 200px;
}

.PokeDetails h1 {
    text-transform: uppercase;
    font-weight: bolder;
    letter-spacing: 1px;
}

The Code block below handles the rendering of the non-modal content. The content itself is clickable and when clicked it loads the modal. After which the modal gets stuck on the UI.

import React from "react";
import "./Pokemon.css"


export const PokemonImage = ({pokemon, loading, pokemonInfo}) => {
  // console.log(pokemon)

  return (
    <>
    {
      loading ? <h1>Loading...</h1>: pokemon.map((data)=>{
        return (
          <>
            <div className="PokemonContainer" key={data.id} 
            onClick={()=>{
              pokemonInfo(data)}}
            >
              <h2>{data.id}</h2>
              <img className="PokemonCard" src={data.sprites.front_default} alt=""/>
              <h2>{data.name}</h2>
            </div>
          </>
        )
      })
    }

    </>
  )
}

The code block below is the main component that handles all the state and renders the other components.

import React from 'react'
import { PokemonImage } from './PokemonImage'
import { PokemonDetails } from './PokemonDetails'
import axios from 'axios'
import { useEffect, useState } from 'react'
import "./Pokemon.css"

export const Main = () => {
    const [pokeData, setPokeData] = useState([]);
    const [loading, setLoading] = useState(true);
    const [url, setUrl] = useState('https://pokeapi.co/api/v2/pokemon?limit=20');
    const [nextUrl, setNextUrl] = useState();
    const [prevUrl, setPrevUrl] = useState();
    const [pokeDexData, setPokeDexData] = useState();
    const [pokeModal, setPokeModal] = useState(false)
    


    const fetchPokeData = async() => {
        setLoading(true);
        const res = await axios.get(url);
        // console.log(res)
        setNextUrl(res.data.next);
        setPrevUrl(res.data.previous);
        getPokeData(res.data.results)
        setLoading(false)
        // console.log(pokeData)
    }

    const getPokeData=async(res)=>{
        setPokeData([])
        res.map(async(item)=>{
            const Pokemons=await axios.get( item.url)
            // console.log(Pokemons.data)
            setPokeData(state =>{
                state=[...state, Pokemons.data]
                state.sort((a,b)=>a.id>b.id?1:-1)
                return state
            })
        })
    }


    useEffect(() => {

        fetchPokeData ();
    }, [url])

    return (
        <>
            <div className='MainContainer'>
                <div className='PokedexContainer'>
                    <PokemonImage  pokemon={pokeData} loading={loading} pokemonInfo={poke=>setPokeDexData(poke)}/>
                    <div className='button-group'>
                        <button onClick={()=>{
                            setPokeData([])
                            setUrl(prevUrl)
                        }}>Previous</button>
                        <button onClick={()=>{
                            setPokeData([])
                            setUrl(nextUrl)
                        }}>Next</button>
                    </div>
                </div>

                <div className='PokeDetails'>
                    {pokeModal === true && (
                        <PokemonDetails setPokeModal={setPokeModal} data={pokeDexData}/>
                    )}
                    <PokemonDetails setPokeModal={setPokeModal} data={pokeDexData}/>
                </div>
            </div>
        </>
    )
}

Originally when I made this app I render all the data to the right side using State. This works fine and state is update according and renders new pokemon data when a different pokemon is clicked. However, when I comment the modal back in. The modal loads when a pokemon is clicked but is then stuck on the UI.

I have tried using state to update the state of the modal with a boolean value. However, I was not successful. I suspect that I am missing a line of code somewhere to re-render the modal when the close button is clicked. However, it just isn't clicking no matter what I try. Any help is appreciated and the full code and be found at the GitHub link provided.


Solution

  • it should look something like this:

    let [modalOpen, setModalOpen] = useState(true)
    
    return <>
      {modalOpen && <div className="Modal"/>}
      <div className="close-btn"><button onClick={() => setModalOpen(false)}>X</button></div>
    </>