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

ReactJS中如何通过Start按钮触发Dice组件的指定函数

React新手问题:点击按钮触发子组件函数执行

我是React新手,正在从零搭建应用,目前遇到技术瓶颈:需点击Footer组件的Start按钮,触发Dice组件内的mortalWoundDecider和objectiveDecider函数执行。当前若直接执行这两个函数(不加function声明),它们会在回合数或目标数更新时自动运行,希望通过Start按钮控制其执行时机。

相关代码

import "./../App.css";
import TextField from '@mui/material/TextField';
import { IconButton, ButtonGroup, Button } from '@mui/material';
import AddIcon from '@mui/icons-material/Add';
import RemoveIcon from '@mui/icons-material/Remove';
import Box from '@mui/material/Box';
import Body from './Body.js';
import { useState } from 'react'; // 补充遗漏的导入

function Footer () {

    //Constants and States
    let [round, setRound] = useState(0);
    let [objectiveValue, setObjectiveValue] = useState(0);
    let [passedValue, setPassedValue] = useState({objective: 0, roundValue: 0});
    const changeState = () => {  
        setPassedValue({objective: objectiveValue, roundValue: round}); 
    };

    //Functions for Incrementing and Decrementing Rounds
    function incrementRound() {
        round = round + 1;
        setRound(round);
    }

    function decrementRound() {
        if(round === 0){
            round = 0;
            setRound(round);
        }
        else{
            round = round - 1;
            setRound(round);
        }
    }

    //Clear Function to run on reset
    function clear(){
        setObjectiveValue(0);
        setRound(0);
        setPassedValue({objective: 0, roundValue: 0})
    }

    //Buttons that are stacked for round addition and subtraction as well as start and reset
    const buttons = [
        <IconButton aria-label="add" size="small" onClick={() => incrementRound()}>
            <AddIcon fontSize="inherit" />
        </IconButton>,
        <IconButton aria-label="remove" size="small" onClick={() => decrementRound()}>
            <RemoveIcon fontSize="inherit" />
        </IconButton>,
    ];

    const buttons2 = [
        <Button variant="text" color="success" onClick={() => {changeState()}}>Start</Button>,
        <Button variant="text" color="error" onClick={() => {clear()}}>Reset</Button>
    ];

        return (
            <div>
                <div class="App-body">
                    <Body objective={passedValue.objective} roundValue={passedValue.roundValue}/>
                </div>

                <div class="App-footer">
                    <Box sx={{ width: 1 }}>
                        <Box display="grid" gridTemplateColumns="repeat(12, 1fr)" gap={2}>
                            <Box gridColumn="span 9">
                            </Box>
                            <Box gridColumn="span 1">
                                <TextField
                                    id="outlined-number"
                                    label="Number of Objectives"
                                    type="number"
                                    InputLabelProps={{
                                        shrink: true,
                                    }}
                                    variant="standard"
                                    sx={{float: "left", marginTop: "12px"}}
                                    onChange={(newValue) => setObjectiveValue(newValue.target.value)}
                                    value = {objectiveValue}
                                />
                            </Box>
                            <Box gridColumn="span 1">
                                <ButtonGroup
                                        orientation="vertical"
                                        aria-label="vertical contained button group"
                                        variant="text"
                                        sx={{float: "left"}}
                                    >
                                        {buttons2}   
                                </ButtonGroup>
                            </Box>
                            <Box gridColumn="span 1">
                            </Box>
                            <Box gridColumn="span 9">
                            </Box>
                            <Box gridColumn="span 1">
                                <h4 className='vertical-center' style={{textAlign: "left", bottom: "58px"}}>Round: {round}</h4>
                            </Box>
                            <Box gridColumn="span 1">
                                <ButtonGroup
                                    orientation="vertical"
                                    aria-label="vertical contained button group"
                                    variant="text"
                                    sx={{float: "left"}}
                                >
                                    {buttons}
                                </ButtonGroup>
                            </Box>
                            <Box gridColumn="span 1">
                            </Box>
                        </Box>
                    </Box>
                </div>
            </div>
        );
}

export default Footer;

Dice.js(处理骰子逻辑组件)

import React from 'react';
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';
import Divider from '@mui/material/Divider';

function Dice(props) {

    //Variables needed for Dice Function
    const min = 1;
    const max = 6; 
    const objectiveNumber = props.objective;
    const roundNumber = props.roundNum;
    let passedObjectives = [];
    let mortalWoundRoll = [];
    
    function diceRoll(){
        return Math.floor(min + Math.random() * (max - min));
    };

    function objectiveDecider(){
        for(var i = 0; i < objectiveNumber; i++){
            let currentDice= diceRoll();
            if(currentDice>=5){
                passedObjectives.push(i);
            }
            else{
            };
        };
    }

    function mortalWoundDecider(){
        for(var z = 0; z < passedObjectives.length; z++){
            let woundArray = [];
            for(var r = 0; r < roundNumber; r++){
                let currentMWDice = diceRoll();
                woundArray.push(currentMWDice);
            };
            mortalWoundRoll.push(woundArray);
        };
    }

    return (
        <div>
            <Row>
                <Col><h3>Objective Number</h3></Col>
                <Col xs={6}><h3>Dice</h3></Col>
                <Col><h3>Mortal Wound Number</h3></Col>
            </Row>
            <Divider />
        </div> 
    );
}

export default Dice;

解决方案

核心思路是通过触发状态控制Dice组件中函数的执行时机,避免随props更新自动运行,具体修改如下:

1. 给Footer组件添加触发状态并传递

修改Footer.js,新增控制骰子滚动的状态,点击Start时触发:

import { useState } from 'react'; // 确保导入useState

function Footer () {
    // ... 原有状态
    const [shouldRoll, setShouldRoll] = useState(false); // 新增触发状态

    const changeState = () => {  
        setPassedValue({objective: objectiveValue, roundValue: round}); 
        setShouldRoll(true); // 点击Start时激活触发状态
    };

    // 修正回合增减函数,用函数式更新避免状态不同步
    function incrementRound() {
        setRound(prev => prev + 1);
    }

    function decrementRound() {
        setRound(prev => prev > 0 ? prev - 1 : 0);
    }

    // 修正clear函数,同时重置触发状态
    function clear(){
        setObjectiveValue(0);
        setRound(0);
        setPassedValue({objective: 0, roundValue: 0});
        setShouldRoll(false);
    }

    // 处理输入转数字,避免字符串问题
    const handleObjectiveChange = (e) => {
        const num = parseInt(e.target.value) || 0;
        setObjectiveValue(num);
    };

    // 修改Body组件的props,传递触发状态和重置回调
    return (
        <div>
            <div className="App-body">
                <Body 
                    objective={passedValue.objective} 
                    roundValue={passedValue.roundValue}
                    shouldRoll={shouldRoll}
                    onRollComplete={() => setShouldRoll(false)}
                />
            </div>
            {/* 修改TextField的onChange为handleObjectiveChange */}
            <TextField
                // ... 其他属性不变
                onChange={handleObjectiveChange}
                value={objectiveValue}
            />
            {/* 其他代码不变 */}
        </div>
    );
}

2. 修改Dice组件,用useEffect监听触发状态

将Dice里的普通变量改为状态,确保数据持久化,同时监听触发状态执行函数:

import React, { useState, useEffect } from 'react';
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';
import Divider from '@mui/material/Divider';

function Dice(props) {
    const { objective, roundNum, shouldRoll, onRollComplete } = props;
    const min = 1;
    const max = 6; 
    // 用state存储结果,避免重渲染丢失
    const [passedObjectives, setPassedObjectives] = useState([]);
    const [mortalWoundRoll, setMortalWoundRoll] = useState([]);
    
    function diceRoll(){
        return Math.floor(min + Math.random() * (max - min));
    };

    function objectiveDecider(){
        const newPassed = [];
        for(let i = 0; i < objective; i++){
            const currentDice = diceRoll();
            if(currentDice >= 5){
                newPassed.push(i);
            }
        };
        setPassedObjectives(newPassed);
        return newPassed;
    }

    function mortalWoundDecider(passedObjs){
        const newWounds = [];
        for(let z = 0; z < passedObjs.length; z++){
            const woundArray = [];
            for(let r = 0; r < roundNum; r++){
                woundArray.push(diceRoll());
            };
            newWounds.push(woundArray);
        };
        setMortalWoundRoll(newWounds);
    }

    // 监听shouldRoll,触发时执行骰子逻辑
    useEffect(() => {
        if(shouldRoll && objective > 0 && roundNum > 0){
            const passed = objectiveDecider();
            mortalWoundDecider(passed);
            onRollComplete(); // 执行完成后通知父组件重置触发状态
        }
    }, [shouldRoll, objective, roundNum, onRollComplete]);

    // 渲染结果示例
    return (
        <div>
            <Row>
                <Col><h3>Objective Number</h3></Col>
                <Col xs={6}><h3>Dice</h3></Col>
                <Col><h3>Mortal Wound Number</h3></Col>
            </Row>
            <Divider />
            {passedObjectives.map((_, idx) => (
                <Row key={idx}>
                    <Col>{idx + 1}</Col>
                    <Col xs={6}>Passed</Col>
                    <Col>{mortalWoundRoll[idx]?.join(', ') || '-'}</Col>
                </Row>
            ))}
        </div> 
    );
}

export default Dice;

3. 确保Body组件传递props到Dice

如果Body只是中转组件,需要把shouldRoll和onRollComplete传递给Dice:

// Body.js
import Dice from './Dice';

function Body(props) {
    return (
        <div>
            <Dice 
                objective={props.objective}
                roundNum={props.roundValue}
                shouldRoll={props.shouldRoll}
                onRollComplete={props.onRollComplete}
            />
        </div>
    );
}

export default Body;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 15:54:36