UseState Will Not Update an Array or Object on First Iteration

Viewed 28

I have faced this problem in the past, an often times I'm not quite sure what solved it, or I need to go extremely out of my way just to update the state for the first time. Currently, I am working with a state that begins as an empty object...

// The Object for the Calendar
const [markedDateObjects, setMarkedDateObjects] = useState({})

I then have this state populated during a useEffect. It will run through an existing array, and for each element in this array, it will add a new key to the object.


// Sets the marked date objects
useEffect(() => {
    meetingDays.forEach(meeting => {
        console.log(meeting)
        setMarkedDateObjects({
            ...markedDateObjects, [meeting]: {marked: true, dotColor: COLORS.gradientColor1, color: 'rbga(255, 255, 255, .30)'}
        })
        console.log("Should change")
    })
    setLoading(false)
}, [meetingDays])


// Prints when marked date object is changed
useEffect(() => {
    console.log(markedDateObjects, "THIS IS STATE")
}, [markedDateObjects])

When the page is loaded, the terminal prints out...

 LOG  2022-09-12
 LOG  Should change
 LOG  2022-09-14
 LOG  Should change
 LOG  {"2022-09-14": {"color": "rbga(255, 255, 255, .30)", "dotColor": "#FFCB05", "marked": true}} THIS IS STATE
 LOG  {"2022-09-14": {"color": "rbga(255, 255, 255, .30)", "dotColor": "#FFCB05", "marked": true}} THIS IS STATE

This shows that it hits two dates, but only adds one to the state. It does not replace the first value, since there is no "THIS IS STATE" line printed beneath the first date. Does anyone understand what's going on here?

1 Answers

Problem

markedDateObjects has the same value in each iteration, so only the last iteration really do something

Solution

Pass a function instead of a value

meetingDays.forEach(meeting => {
  setMarkedDateObjects(markedDateObjects => ({
    ...markedDateObjects, [meeting]: {marked: true, dotColor: COLORS.gradientColor1, color: 'rbga(255, 255, 255, .30)'}
  })
})

Gather the changes and set the state once

const newMarkedDateObjects = { ...markedDateObjects }
meetingDays.forEach(meeting => {
  newMarkedDateObjects[meeting] = {marked: true, dotColor: COLORS.gradientColor1, color: 'rbga(255, 255, 255, .30)'}
})
setMarkedDateObjects(newMarkedDateObjects)
Related