从 parent 访问 children 个属性(状态?)

Access children properties (state?) from parent

我正在构建我的第一个 React 应用程序,我对如何在组件之间进行通信有点困惑。

本应用计算玩家某场比赛的得分。为此,我创建了一个组件数组,然后我需要对 parent 组件中每个组件的分数求和。

各组件代码如下:

Parent:

const RouteScore = () => {
    const routes = [
        { length: 1, score: 1 },
        { length: 2, score: 2 },
        { length: 3, score: 4 },
        { length: 4, score: 7 },
        { length: 6, score: 15 },
        { length: 8, score: 21 },
    ]

    const buttons = routes.map((route) => <RouteScoreButton length={route.length} score={route.score} key={route.length} />)

    return (
        <div className="position-absolute top-50 start-50 translate-middle">
            <div className="bg-light border border-primary rounded-3 px-4 py-4 opacity-75 text-center">
                <table className="table">
                    <thead>
                        <tr className="text-center">
                            <th scope="col">Route length</th>
                            <th scope="col">Total routes</th>
                            <th scope="col">Total score</th>
                        </tr>
                    </thead>
                    <tbody className="text-center align-middle">
                        {buttons}
                    </tbody>
                </table>
                <h3>Total score:</h3>
            </div>
        </div>
    )
}

Children:

const RouteScoreButton = (props) => {
    const [numberRoutes, setNumberRoutes] = useState(0)
    const score = useMemo(() => props.score * numberRoutes, [numberRoutes])

    const increaseRoute = () => {
        setNumberRoutes(numberRoutes + 1)
    }
    const decreaseRoute = () => {
        numberRoutes > 0 && setNumberRoutes(numberRoutes - 1)
    }

    const handleChange = (e) => {
        let newValue = parseInt(e.target.value)
        if (isNaN(newValue)) {
            setNumberRoutes(0)
        } else {
            setNumberRoutes(parseInt(e.target.value))
        }
    }

    return (
        <tr>
            <th>
                <h3>{props.length}</h3>
            </th>
            <th>
                <div className="input-group" role="group" aria-label="Basic example">
                    <button type="button" className="btn btn-primary" onClick={increaseRoute}>+</button>
                    <input type="text" className="form-control text-center" style={{ width: '50px' }} value={numberRoutes} onChange={handleChange} />
                    <button type="button" className="btn btn-primary" onClick={decreaseRoute}>-</button>
                </div>
            </th>
            <th>
                <h3>{score}</h3>
            </th>
        </tr>
    )
}

如何计算每个 child 部分的分数总和?

在这种情况下,最好将状态向上移动一级,因此在 parent 中它将是

const RouteScore = () => {
    const [routes, setRoutes] = useState([
        { length: 1, score: 1, numberRoutes: 0 },
        { length: 2, score: 2, numberRoutes: 0 },
        { length: 3, score: 4, numberRoutes: 0 },
        { length: 4, score: 7, numberRoutes: 0 },
        { length: 6, score: 15, numberRoutes: 0 },
        { length: 8, score: 21, numberRoutes: 0 },
    ]);

    const handleRouteChange = (length, numberRoutes) => {
       const newRoutes = routes.map(elem => 
            if (elem.length === length){
                return {...elem, score: numberRoutes*length, numberRoutes}
            }
            return elem;
       )
       setRoutes(newRoutes);
    }

    const buttons = routes.map((route) => 
       <RouteScoreButton length={route.length} score={route.score} key={route.length} handleRouteChange={handleRouteChange}/>)

    return (
        <div className="position-absolute top-50 start-50 translate-middle">
            <div className="bg-light border border-primary rounded-3 px-4 py-4 opacity-75 text-center">
                <table className="table">
                    <thead>
                        <tr className="text-center">
                            <th scope="col">Route length</th>
                            <th scope="col">Total routes</th>
                            <th scope="col">Total score</th>
                        </tr>
                    </thead>
                    <tbody className="text-center align-middle">
                        {buttons}
                    </tbody>
                </table>
                <h3>Total score:</h3>
            </div>
        </div>
    )
}

并在 child

const RouteScoreButton = (props) => {
  
    const increaseRoute = () => {
        props.handleRouteChange(props.length, props.numberRoutes + 1)
    }
    const decreaseRoute = () => {
        props.numberRoutes > 0 && props.handleRouteChange(props.length, props.numberRoutes - 1)
    }

    const handleChange = (e) => {
        let newValue = parseInt(e.target.value)
        if (isNaN(newValue)) {
            props.handleRouteChange(props.length, 0)
        } else {
            props.handleRouteChange(props.length, parseInt(e.target.value))
        }
    }

    return (
        <tr>
            <th>
                <h3>{props.length}</h3>
            </th>
            <th>
                <div className="input-group" role="group" aria-label="Basic example">
                    <button type="button" className="btn btn-primary" onClick={increaseRoute}>+</button>
                    <input type="text" className="form-control text-center" style={{ width: '50px' }} value={numberRoutes} onChange={handleChange} />
                    <button type="button" className="btn btn-primary" onClick={decreaseRoute}>-</button>
                </div>
            </th>
            <th>
                <h3>{score}</h3>
            </th>
        </tr>
    )
}

这将是一个更好的方法。

出于某种原因,如果您仍希望 parent 访问 child 状态。你可以检查 useImperativeHandle 钩子。