如何将CountDown子组件的onTime状态传递给QuestionCard父组件?
解决CountDown子组件向QuestionCard父组件传递倒计时状态的问题
核心思路
React中子组件向父组件传递状态,最直接的方式是通过父组件传递回调函数给子组件,子组件在状态变化时调用该回调,将新状态传递回去。
具体修改步骤
1. 修改CountDown子组件,添加状态回调传递
在CountDown组件中,接收父组件传入的回调函数,当onTime状态变化时调用该函数,把最新状态传给父组件:
import { Typography, Paper, Grid } from '@mui/material'; import React, { useEffect, useRef, useState } from 'react'; const formatTime = (time) => { let minutes = Math.floor(time / 60); let seconds = Math.floor(time - minutes * 60); if (minutes < 10) { minutes = '0' + minutes; } if (seconds < 10) { seconds = '0' + seconds; } return minutes + ':' + seconds; }; function CountDown(props) { const [countdown, setCountdown] = useState(props.seconds); const [onTime, setOnTime] = useState(true); const timertId = useRef(); useEffect(() => { timertId.current = setInterval(() => { setCountdown((prev) => prev - 1); }, 1000); return () => clearInterval(timertId.current); }, []); useEffect(() => { if (countdown <= 0) { clearInterval(timertId.current); setOnTime(false); // 调用父组件传入的回调,传递onTime状态 props.onTimeChange(false); } }, [countdown, props.onTimeChange]); // 组件初始化时传递初始状态,确保父子组件状态一致 useEffect(() => { props.onTimeChange(true); }, [props.onTimeChange]); return ( <Grid container> <Grid item xs={5}> <Paper elevation={0} variant='outlined' square> <Typography component='h6' fontFamily={'Roboto'}> Timer: </Typography> </Paper> </Grid> <Grid item xs={5}> <Paper elevation={0} variant='outlined' square sx={{ bgcolor: 'lightblue' }} > <Typography component='h6' fontFamily={'Roboto'}> {formatTime(countdown)} </Typography> </Paper> </Grid> </Grid> ); } export default CountDown;
2. 修改QuestionCard父组件,接收子组件状态并控制渲染
在父组件中添加onTime状态,传递回调给CountDown,然后根据onTime的值控制显示内容:
import { Grid, Box, Card, CardContent, Typography, LinearProgress, ButtonGroup, ListItemButton, CardActions, Button } from '@mui/material'; import React, { useState, useEffect } from 'react'; import CountDown from './CountDown'; // 确保组件路径正确 export default function QuestionCard() { const [questions, setQuestions] = useState([]); const [clickedIndex, setClickedIndex] = useState(0); const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0); const [value, setValue] = useState(null); // 添加onTime状态,初始值与子组件保持一致 const [onTime, setOnTime] = useState(true); const { isLoading, error, sendRequest: getQuestions } = useAxios(); const { sendRequest: getAnswers } = useAxios(); const handleSubmit = () => { setValue(true); }; const handleSelectedItem = (index) => { setClickedIndex(index); }; const handleChange = (e) => { setValue(e.target.value); }; // 补全跳转下一题的函数实现 const goToNext = () => { setCurrentQuestionIndex(prev => prev + 1); setValue(null); setClickedIndex(0); }; useEffect(() => { const transformQuestions = (questionObj) => { const loadedQuestions = []; for (const questionKey in questionObj) { loadedQuestions.push({ id: questionKey, id_test: questionObj[questionKey].id_test, tipologia_domanda: questionObj[questionKey].tipologia_domanda, testo: questionObj[questionKey].testo, immagine: questionObj[questionKey].immagine, eliminata: questionObj[questionKey].eliminata, }); } setQuestions(loadedQuestions); }; getQuestions( { method: 'GET', url: baseURL_Q, }, transformQuestions ); }, [getQuestions]); let questionsTitle = questions.map((element) => `${element.testo}`); let questionId = questions.map((element) => `${element.id}`); return ( <Grid container spacing={1}> <Grid item xs={10}> <Box sx={{ minWidth: 275, display: 'flex', alignItems: 'center', paddingLeft: '50%', paddingBottom: '5%', position: 'center', }} > <Card variant='outlined' sx={{ minWidth: 400, }} > <CardContent> <Grid container spacing={0}> <Grid item xs={8}> <Typography variant='h5' component='div' fontFamily={'Roboto'} > Nome Test </Typography> </Grid> <Grid item xs={4}> {/* 传递回调函数给子组件 */} <CountDown seconds={300} onTimeChange={setOnTime} /> </Grid> </Grid> {/* 根据onTime状态切换显示内容 */} {onTime ? ( <> <LinearProgress variant='determinate' value={1} /> <Typography sx={{ mb: 1.5, mt: 1.5 }} fontFamily={'Roboto'} fontWeight={'bold'} > {questionsTitle[currentQuestionIndex]} </Typography> <ButtonGroup fullWidth orientation='vertical' onClick={handleSubmit} onChange={handleChange} > <ListItemButton selected={clickedIndex === 1} onClick={() => handleSelectedItem(1)} > Risposta 1 </ListItemButton> <ListItemButton selected={clickedIndex === 2} onClick={() => handleSelectedItem(2)} > Risposta 2 </ListItemButton> <ListItemButton selected={clickedIndex === 3} onClick={() => handleSelectedItem(3)} > Risposta 3 </ListItemButton> <ListItemButton selected={clickedIndex === 4} onClick={() => handleSelectedItem(4)} > Risposta 4 </ListItemButton> </ButtonGroup> </> ) : ( {/* 超时提示,可替换为弹窗或自定义组件 */} <Typography variant='h6' sx={{ mt: 4, textAlign: 'center', color: 'red' }} fontFamily={'Roboto'} > Tempo scaduto! Il test è terminato. </Typography> )} </CardContent> {/* 仅在倒计时未结束时显示提交按钮 */} {onTime && ( <CardActions> <Button onClick={goToNext} disabled={!value} variant='contained' size='small'> Avanti </Button> </CardActions> )} </Card> </Box> </Grid> </Grid> ); }
关键说明
- 子组件通过调用父组件传入的
onTimeChange回调,将自身状态同步给父组件 - 父组件利用
onTime状态实现条件渲染,完成倒计时结束后的内容切换 - 修正了原
formatTime函数的补0逻辑(原代码<=10会导致10变成010,改为<10更合理) - 补全了父组件中缺失的
goToNext函数实现,避免运行报错
内容的提问来源于stack exchange,提问作者Fabrizio Mastrone
相关产品推荐
相关产品推荐

