When I am updating an array in React useState, it keeps defaulting to the initial state. What am I doing wrong?

Viewed 77

I have a state array, monthlyIncidents, and inside of this array are 12 numbers that I want to update based on some condition met. After researching online, I found that one way to accomplish this was to copy the array, update the element, and then finally update the state. I tried this, however it is not working correctly.

const [monthlyIncidents, setMonthlyIncidents] = useState([
    // monthlyIncidents[0] -> January ... monthlyIncidents[11] -> December
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
  ])

In my useEffect implementation, I am looping over a set of records (a total of 3 times), and I want to update whatever element is at position 'incidentMonth' of the monthlyIncidents array. Furthermore, from my dummy data I know that at the end of the loop, the element at index 7 (August) should have a value of 3.

useEffect(() => {
    if (data === undefined || isLoading) {
      console.log('data is undefined')
    } else {
      const totalIncidents: number = data.length
      for (let incident = 0; incident < totalIncidents; incident += 1) {
        let incidentMonth = getIncidentMonth(data[incident].reportedOn)
        handleUpdate(incidentMonth)
      }
    }
  }, [data])

This is what my handleUpdate function looks like:

const handleUpdate = (incidentMonth: number) => {
    console.log(monthlyIncidents: ", monthlyIncidents)
    var newMonthlyIncidents = [...monthlyIncidents]
    newMonthlyIncidents[incidentMonth] += 1
    console.log("newMonthlyIncidents: ", newMonthlyIncidents)
    setMonthlyIncidents(newMonthlyIncidents)
  }

I will now post the output found in the console. somehow monthlyIncidents is going back to its initial state (where all elements are storing 0

iteration number  0
VisualizeIncidents.tsx:29 monthlyIncidents: (12) [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
VisualizeIncidents.tsx:32 newMonthlyIncidents:  (12) [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]
VisualizeIncidents.tsx:48 iteration number  1
VisualizeIncidents.tsx:29 monthlyIncidents: (12) [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
VisualizeIncidents.tsx:32 newMonthlyIncidents:  (12) [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]
VisualizeIncidents.tsx:48 iteration number  2
VisualizeIncidents.tsx:29 monthlyIncidents: (12) [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
VisualizeIncidents.tsx:32 newMonthlyIncidents:  (12) [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]


The entire component as requested

import React, { useEffect, useState } from 'react'

import useIncidents from '../hooks/useIncidents'
import IncidentFilter from '../IncidentFilter'
import IncidentSearchRequest from '../model/IncidentSearchRequest'

const VisualizeIncidents = () => {
  const searchFilter = IncidentFilter.reported
  const searchRequest: IncidentSearchRequest = { status: searchFilter }
  const { data, isLoading } = useIncidents(searchRequest)
  const [monthlyIncidents, setMonthlyIncidents] = useState([
    // monthlyIncidents[0] -> January ... monthlyIncidents[11] -> December
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
    0,
  ])

  const handleUpdate = (incidentMonth: number) => {
    var newMonthlyIncidents = [...monthlyIncidents]
    newMonthlyIncidents[incidentMonth] += 1
    console.log('newMonthlyIncidents: ', newMonthlyIncidents)
    console.log('monthlyIncidents before:', monthlyIncidents)
    setMonthlyIncidents(newMonthlyIncidents)
    console.log('monthlyIncidents after:', monthlyIncidents)
  }

  const getIncidentMonth = (reportedOn: string) => {
    // reportedOn: "2020-08-12T19:53:30.153Z"
    return Number(reportedOn.slice(5, 7)) - 1
  }

  useEffect(() => {
    if (data === undefined || isLoading) {
      console.log('data is undefined')
    } else {
      const totalIncidents: number = data.length
      for (let incident = 0; incident < totalIncidents; incident += 1) {
        const incidentMonth = getIncidentMonth(data[incident].reportedOn)
        console.log('iteration number ', incident)
        handleUpdate(incidentMonth)
      }
    }
  }, [data])

  // console.log("after updating: ", monthlyIncidents)

  return (
    <>
      <LineGraph
        datasets={[
          {
            backgroundColor: 'blue',
            borderColor: 'black',
            data: [
              {
                x: 'January',
                y: 12,
              },
              {
                x: 'February',
                y: 11,
              },
              {
                x: 'March',
                y: 10,
              },
            ],
            label: 'Incidents',
          },
        ]}
        title="Reported Incidents Overtime"
        xAxes={[
          {
            label: 'Months',
            type: 'category',
          },
        ]}
        yAxes={[
          {
            label: 'Numbers',
            type: 'linear',
          },
        ]}
      />
    </>
  )
}

export default VisualizeIncidents

1 Answers

setMonthlyIncidents((prevIncidents) => {
   return { ...prevIncidents, [incidentMonth]: monthlyIncidents[incidentMonth] += 1 }
})

No need for a handleUpdate function

Credit to: amakhrov

Resource: reactjs.org/docs/hooks-reference.html#functional-updates

Related