Reputation: 495
I'm building my first React app, and I'm a little confused in how to talk between components.
The app calculates the score of the player of certain game. For this, I create an array of components and then I need to sum the scores of each in the parent component.
The code of each component is the following:
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>
)
}
How can I calculate the sum of scores of every child component?
Upvotes: 0
Views: 79
Reputation: 374
In this case its better to move the state one level up, So in parent it would be
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>
)
}
and in 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>
)
}
This will be a better approach.
For some reason if you still want parent to access the child state. you can check useImperativeHandle hook.
Upvotes: 1