You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React应用中Modal无法关闭的问题求助

React模态框无法关闭的问题修复

我是软件开发新手,正在使用ReactJS结合Pokeapi接口开发模态框UI组件,已通过State和Axios成功加载数据,但实现的模态框打开后无法关闭,尝试过用布尔值状态控制但未成功。

问题代码分析

Modal组件代码(原错误版本)

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

export const PokemonDetails = ({data, setPokeModal}) => {
 
  console.log(data)
  const checkClose = () => {
    setPokeModal = false // 错误:直接赋值,未调用setState函数
    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>
          </>
        )
      }
    </>
  )
}

主组件代码(原错误版本)

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);
        setNextUrl(res.data.next);
        setPrevUrl(res.data.previous);
        getPokeData(res.data.results)
        setLoading(false)
    }

    const getPokeData=async(res)=>{
        setPokeData([])
        res.map(async(item)=>{
            const Pokemons=await axios.get( item.url)
            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>
        </>
    )
}

修复步骤

1. 修正Modal组件的关闭逻辑

setState是函数,不能直接赋值,需要调用setPokeModal(false),同时优化组件渲染逻辑:

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

export const PokemonDetails = ({data, setPokeModal}) => {
 
  const handleClose = () => {
    setPokeModal(false) // 正确调用setState函数
  }

  const handleModalClick = (e) => {
    // 阻止点击模态框内部时触发关闭
    e.stopPropagation();
  }
  
  if(!data) return null;
  
  return (
    <div className="BackShadow" onClick={handleClose}>
      <div className='Modal' onClick={handleModalClick}>
        <div className='close-btn'><button onClick={handleClose}>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={data.name}/>
        </div>
        <div className='TypesContainer'>
          {
            data.types.map((pokemon, index)=>{
              return (
                <h2 key={index}>
                  {pokemon.type.name}
                </h2>
              )
            })
          }
        </div>
      </div>
    </div>
  )
}

2. 修正主组件的渲染逻辑

  • 移除重复的PokemonDetails渲染,只保留条件渲染的部分
  • 在PokemonImage的点击事件中添加打开模态框的逻辑:
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);
        setNextUrl(res.data.next);
        setPrevUrl(res.data.previous);
        getPokeData(res.data.results)
        setLoading(false)
    }

    const getPokeData=async(res)=>{
        setPokeData([])
        res.map(async(item)=>{
            const Pokemons=await axios.get( item.url)
            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);
                          setPokeModal(true); // 添加打开模态框的逻辑
                        }}
                    />
                    <div className='button-group'>
                        <button onClick={()=>{
                            setPokeData([])
                            setUrl(prevUrl)
                        }}>Previous</button>
                        <button onClick={()=>{
                            setPokeData([])
                            setUrl(nextUrl)
                        }}>Next</button>
                    </div>
                </div>

                <div className='PokeDetails'>
                    {/* 只有模态框打开且有数据时才渲染 */}
                    {pokeModal && pokeDexData && (
                        <PokemonDetails setPokeModal={setPokeModal} data={pokeDexData}/>
                    )}
                </div>
            </div>
        </>
    )
}

修复说明

  • Modal组件中错误地直接给setState函数赋值,改为调用函数更新状态
  • 主组件中重复渲染了PokemonDetails,导致关闭模态框后仍有内容显示,移除重复渲染
  • 点击宝可梦卡片时,不仅设置详情数据,还要将模态框状态设为true,触发模态框显示
  • 添加点击遮罩层关闭模态框的逻辑,提升交互体验

内容的提问来源于stack exchange,提问作者Lockdown1337

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 20:09:26