React卡牌游戏开发:Props异步致子组件取值undefined问题求助
问题描述
在开发React卡牌游戏时遇到以下问题:
- 父组件
Opponents.jsx维护了opponents数组状态(格式如[name, name1]),通过props传递给子组件OpponentCards.jsx - 子组件的return渲染部分能正确读取
props.opponent并显示对手名称,但在return之前的代码(比如console.log(props.opponent))中访问时,值为undefined - 已确认是props异步更新导致,但不清楚正确解决方式;另外在VSCode中刷新Vite但不刷新页面时,问题会暂时消失
相关代码
Opponents.jsx
import React, { useEffect, useState } from 'react' import OpponentCards from './OpponentCards' const Opponents = (props) => { const [opponents, setOpponents] = useState([]) const [numberOfPlayers, setNumberOfPlayers] = useState('two') let updateUsers = (data) => { let usersToChange = data.users if(usersToChange.find(item => item === localStorage.getItem('name'))){ while(String(usersToChange[0]) !== localStorage.getItem('name')){ usersToChange.push(usersToChange[0]) usersToChange.splice(0,1) } } setOpponents(usersToChange) switch (props.users.length) { case 2 : setNumberOfPlayers('two') return case 3 : setNumberOfPlayers('three') return case 4 : setNumberOfPlayers('four') return } } props.socket.on('getUsersResponse', (data) => { updateUsers(data) }) useEffect(() => updateUsers(props) ,[props.users]) console.log(opponents) //correct array of opponents return ( <div className={numberOfPlayers+'-players'}> <div className="opponent one"> <OpponentCards opponent = {opponents[1]} number={1} socket= {props.socket}/> </div> <div className="opponent two"> <OpponentCards opponent = {opponents[2]} number={3} socket= {props.socket}/> </div> <div className="opponent three"> <OpponentCards opponent = {opponents[2]} number={3} socket= {props.socket}/> </div> </div> ) } export default Opponents
OpponentCards.jsx
import React, { useEffect, useState } from 'react' import userLogo from '../../sourses/Untitled.svg' const OpponentCards = (props) => { let [cards, setCards] = useState(0) let addCard = (name) => { if(name === props.opponent){ setCards(cards => cards +1) } } useEffect(() => { props.socket.on('getCardFromDeckToOpponentResponse',(name) => addCard(name)) },[props.socket]) console.log(props.opponent)//undefined return ( <> <div className="container"> <h3 className='name'> {props.opponent ? props.opponent : 'Player ' + (props.number + 1)} </h3> <img src={userLogo} /> <div className="opponentCards"> {[...Array(cards)].map((elem, index)=><div key={index} className='card'>x</div>)} </div> </div> </> ) } export default OpponentCards
解决思路及方案
1. 根本原因
- 组件初始化时,父组件
opponents初始值为空数组,此时传递给子组件的opponents[1]、opponents[2]都是undefined,所以子组件第一次渲染时props.opponent为undefined - 父组件通过
socket或useEffect更新opponents状态后,会触发重新渲染并传递更新后的props,但子组件中console.log只在初始渲染时执行一次,因此看不到更新后的值;而return部分会在每次渲染时更新,所以能显示正确名称 - Vite热刷新时会重置组件状态,可能刚好让
opponents在初始化时就有值,导致问题暂时消失
2. 具体修复方案
方案一:监听props变化,确保逻辑在props更新后执行
在子组件OpponentCards.jsx中添加useEffect监听props.opponent的变化,确保在props更新时执行相关逻辑:
useEffect(() => { // 当props.opponent更新时,执行需要的逻辑 console.log('更新后的opponent:', props.opponent); }, [props.opponent]); // 依赖项设为props.opponent,变化时触发
方案二:修复socket回调的闭包陷阱
当前addCard函数在socket回调中使用的是旧的props.opponent(闭包陷阱),修改为直接使用最新props:
useEffect(() => { const handleCardAdd = (name) => { if(name === props.opponent){ setCards(cards => cards +1) } }; props.socket.on('getCardFromDeckToOpponentResponse', handleCardAdd); // 清理函数,防止内存泄漏 return () => { props.socket.off('getCardFromDeckToOpponentResponse', handleCardAdd); }; }, [props.socket, props.opponent]); // 添加props.opponent到依赖项
方案三:父组件添加条件渲染,避免传递undefined
在父组件中,只有当opponents数组长度足够时才渲染对应的子组件,避免传递undefined:
return ( <div className={numberOfPlayers+'-players'}> {opponents.length >= 2 && ( <div className="opponent one"> <OpponentCards opponent={opponents[1]} number={1} socket={props.socket}/> </div> )} {opponents.length >= 3 && ( <div className="opponent two"> <OpponentCards opponent={opponents[2]} number={3} socket={props.socket}/> </div> )} {opponents.length >= 4 && ( <div className="opponent three"> <OpponentCards opponent={opponents[3]} number={3} socket={props.socket}/> </div> )} </div> )
(注:原代码中第三个OpponentCards误用了opponents[2],这里修正为opponents[3])
内容的提问来源于stack exchange,提问作者Dimon 1200
相关产品推荐
相关产品推荐

