0

I have an empty nested array called scorecard in a useState object and I'm trying to fill this array with user input. I'm having issues with the proper way to handle this while maintaining the previous values.

Basically, my question is how can I fill the nested scorecard array, which will also have a nested object in it, and keep the previous values of everything inside the array and nested object.

What I currently have does not maintain the previous values of the nested object 'tees' in the scorecard array.

AddCourse.js

const AddCourse = () => {
const [courseInfo, setCourseInfo] = useState({
    name: "",
    phone: "",
    website: "",
    address: "",
    city: "",
    state: "",
    zip: "",
    country: "",
    holes: "",
    holes: "9",
    teeBoxes: [],
    scorecard: []
});

    const handleSubmit = (e) => {
      // Do something
    }
    return (
        <div className={classes.outerCourseContainer}>
            <Paper elevation={4} className={classes.innerCourseContainer}>
                <Typography variant="h6" gutterBottom>
                    Course Information
                </Typography>
                <Grid container spacing={2}>
                    <Scorecard courseInfo={courseInfo} setCourseInfo={setCourseInfo} />
                    <Grid item xs={12} className={classes.submitCourse}>
                        <Button variant='contained' color='primary' onClick={handleSubmit}>
                            Submit
                        </Button>
                    </Grid>
                </Grid>
            </Paper>
        </div >
    )
}

Scorecard.js

    let scorecardData = [...courseInfo.scorecard]
    let teeBox = {}
    const handleScorecard = (e, hole, holeIndex, tee) => {
        const { name, value } = e.target
        if (name === 'tees') {
            teeBox['teeBox' + tee] = {
                ...teeBox['teeBox' + tee],
                'color': value
            }
        }
    
        if (name === 'yards') {
            teeBox['teeBox' + tee] = {
                ...teeBox['teeBox' + tee],
                'yards': value
            }
        }
    
        if (name === 'par') {
            scorecardData[holeIndex] = {
                ...scorecardData[holeIndex],
                'par': value,
            }
        }
    
        if (name === 'handicap') {
            scorecardData[holeIndex] = {
                ...scorecardData[holeIndex],
                'handicap': value,
                'Hole': hole
            }
        }
    
        scorecardData[holeIndex] = {
            ...scorecardData[holeIndex],
            tees: structuredClone(teeBox)
        }
        setCourseInfo({ ...courseInfo, scorecard: scorecardData })
    }
    
    
    <Table className={classes.scorecardTableContainer} aria-label="scorecard">
                            <TableBody>
                                <TableRow>
                                    <TableCell className={classes.scorecardHolesCell}>Holes</TableCell>
                                    {[1, 2, 3, 4, 5, 6, 7, 8, 9].map((x, i) => (
                                        <TableCell className={classes.scorecardHolesCell} key={i}>
                                            {x}
                                        </TableCell>
                                    ))}
                                </TableRow>
                                {[1, 2, 3].map((tee, teeIndex) => (
                                    <TableRow key={teeIndex}>
                                        <TableCell>
                                            <TextField
                                                inputProps={{ maxLength: 10 }}
                                                id={`teeBoxInput${tee}`}
                                                name="tees"
                                                placeholder={`Tee ${tee}`}
                                                variant="outlined"
                                                onChange={(e) => handleScorecard(e, 0, 0, tee)}
                                            />
                                        </TableCell>
                                        {[1, 2, 3, 4, 5, 6, 7, 8, 9].map((hole, holeIndex) => (
                                            <TableCell key={holeIndex}>
                                                <TextField
                                                    inputProps={{ maxLength: 3 }}
                                                    id={`yardInput${hole}`}
                                                    name="yards"
                                                    fullWidth
                                                    variant="outlined"
                                                    onChange={(e) => handleScorecard(e, hole, holeIndex, tee)}
                                                />
                                            </TableCell>
                                        ))}
                                    </TableRow>
                                ))}
                                <TableRow>
                                    <TableCell className={classes.scorecardParCell}>Par</TableCell>
                                    {[1, 2, 3, 4, 5, 6, 7, 8, 9].map((hole, holeIndex) => (
                                        <TableCell key={holeIndex}>
                                            <TextField
                                                inputProps={{ maxLength: 1 }}
                                                id={`parInput${hole}`}
                                                name="par"
                                                fullWidth
                                                variant="outlined"
                                                onChange={(e) => handleScorecard(e, hole, holeIndex)}
                                            />
                                        </TableCell>
                                    ))}
                                </TableRow>
                                <TableRow>
                                    <TableCell className={classes.scorecardHandicapCell}>Handicap</TableCell>
                                    {[1, 2, 3, 4, 5, 6, 7, 8, 9].map((hole, holeIndex) => (
                                        <TableCell key={holeIndex}>
                                            <TextField
                                                inputProps={{ maxLength: 2 }}
                                                id={`handicapInput${hole}`}
                                                name="handicap"
                                                fullWidth
                                                variant="outlined"
                                                onChange={(e) => handleScorecard(e, hole, holeIndex)}
                                            />
                                        </TableCell>
                                    ))}
                                </TableRow>
                            </TableBody>
                 

   </Table>

Current output of scorecard:

[
    {
        "tees": {
            "teeBox1": {
                "color": "blue",
                "yards": "321"
            },
            "teeBox2": {
                "color": "white",
                "yards": "301"
            }
        },
        "par": "4",
        "handicap": "1",
        "Hole": 1
    },
    {
        "tees": {
            "teeBox1": {
                "color": "blue",
                "yards": "321"
            },
            "teeBox2": {
                "color": "white",
                "yards": "301"
            }
        },
        "par": "4",
        "handicap": "2",
        "Hole": 2
    }
]

Expected output of scorecard:

[
    {
        "tees": {
            "teeBox1": {
                "color": "blue",
                "yards": "444"
            },
            "teeBox2": {
                "color": "white",
                "yards": "333"
            }
        },
        "par": "4",
        "handicap": "1",
        "Hole": 1
    },
    {
        "tees": {
            "teeBox1": {
                "color": "blue",
                "yards": "321"
            },
            "teeBox2": {
                "color": "white",
                "yards": "301"
            }
        },
        "par": "4",
        "handicap": "2",
        "Hole": 2
    }
]
foshesco
  • 23
  • 6
  • This might help: https://stackoverflow.com/questions/56802815/react-hooks-how-do-i-update-state-on-a-nested-object-with-usestate or this one: https://stackoverflow.com/questions/57798841/react-setting-state-for-deeply-nested-objects-w-hooks – CodeThing Mar 28 '23 at 15:24

1 Answers1

0

Figured it out.

Looks like this...

let scorecardData = [...courseInfo.scorecard]
let colorData = [...color]
const handleScorecard = (e, hole, tee) => {
    const { name, value } = e.target
    if ((name === 'color' || name === 'yards')) {
        if (hole === 0) {
            colorData[tee] = value
        }

        if (hole !== 0) {
            scorecardData[hole - 1] = {
                ...scorecardData[hole - 1],
                tees: {
                    ...scorecardData[hole - 1]?.tees,
                    ['teeBox' + tee]: {
                        ...scorecardData[hole - 1]?.tees?.['teeBox' + tee],
                        'color': colorData[tee] || '',
                        'yards': value,
                    }
                }
            }
        }
    }

    if (name === 'par') {
        scorecardData[hole - 1] = {
            ...scorecardData[hole - 1],
            'par': value,
        }
    }

    if (name === 'handicap') {
        scorecardData[hole - 1] = {
            ...scorecardData[hole - 1],
            'handicap': value,
            'Hole': hole
        }
    }

    setColor(colorData)
    setCourseInfo((previous) => ({ ...courseInfo, scorecard: scorecardData }))
}
Tim Consolazio
  • 4,802
  • 2
  • 19
  • 28
foshesco
  • 23
  • 6
  • 1
    One note (I made edit): I think that "courseInfo" is the previous value, and you're overwriting "scoreCard". According to the docs, a course I took, and all that sort of thing, that's not the way you'd want to go about it. You'd want to use this format: setCourseInfo( (previous) => ({ ...previous, scorecard: scoreCardData}) ) There's reasons for this involving how you can "guarantee" the update takes predictably in all contexts. A fine point, but one well worth mentioning; in general, if you are setting state from a prior state, access the prev value this way. – Tim Consolazio Mar 29 '23 at 19:49
  • Shouldn't it be setCourseInfo((previous) => ({ ...previous, scorecard: scorecardData })) instead? I made the change and am seeing it working correctly – foshesco Mar 29 '23 at 20:23
  • 1
    If there was a typo sure. The point was the way of accessing the previous value. – Tim Consolazio Mar 31 '23 at 11:24