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
相关产品推荐
相关产品推荐

