Nextjs/React : Skip useEffect function on particular state change

Viewed 45

I built chart view page which display sales data. This chart will updated through REST API when user change dimension, filter value and period.

useEffect(async () => {
   let [startDate, endDate] = getReportPeriod({target: { innerText: period}}) // get formatted date string
   getReportData(startDate, endDate) // function that call REST API to get chart data
}, [filteredDimensions, dimensions, dimensionTime])

Also, user can save and load chart setting.

const loadCustomReport = async(reportName) => {
   let res = await axios.post('/api/customReport/loadReport', {
      report_name: reportName
   }) // get report setting

   let setting = JSON.parse(res.data[0].report_setting) // parse report setting as json

   setRevenue(setting.metrics.revenue)
   setImpression(setting.metrics.impression)
   setClicks(setting.metrics.clicks)
   setCPM(setting.metrics.CPM)
   setCTR(setting.metrics.CTR)

   setDimensions(setting.dimensions)
   setFilteredDimensions(setting.filteredDimensions)
}

The problem is the useEffect function run twice(both of setDimensions and setFilteredDimensions) and response times vary, so sometimes I got report data that a setting is not fully applied.

Is there any way to skip useEffect function running on setDimensions?

I think put both dimension setting and filter setting on one state might be works but I just curious that there are other solutions.

Please advise me any suggestion to solve the problem.


@Gabriele Petrioli give me the solution actually I wanted.
@man.in.the.jukebox and @Brandon Pelton-Cox's advice give me a great point.

Currently my code is as below(I changed before I saw @Gabriele Petrioli's answer)

useEffect(async () => {
        let [startDate, endDate] = getReportPeriod({target: { innerText: period}})
        getReportData(startDate, endDate)
}, [filteredDimensions, dimensionTime])

// button handler that add dimension to current dimensions state
const addDimension = (dimension) => {
   setDimensions([
      ...dimensions,
      dimension
   ])
   let [startDate, endDate] = getReportPeriod({target: { innerText: period}})
   getReportData(startDate, endDate)
}

// button handler that remove dimension from current dimensions state
const removeDimension = (idx) => {        
   let tempDimensions = [...dimensions]
   tempDimensions.splice(idx, 1)
   setDimensions(tempDimensions)
   let [startDate, endDate] = getReportPeriod({target: { innerText: period}})
   getReportData(startDate, endDate)
}
// load report setting
const loadCustomReport = async(reportName) => {
   let res = await axios.post('/api/customReport/loadReport', {
      report_name: reportName
   })

   let setting = JSON.parse(res.data[0].report_setting)

   setRevenue(setting.metrics.revenue)
   setImpression(setting.metrics.impression)
   setClicks(setting.metrics.clicks)
   setCPM(setting.metrics.CPM)
   setCTR(setting.metrics.CTR)
   setDimensions(setting.dimensions)
   setFilteredDimensions(setting.filteredDimensions)
}

Thank you all for give me great advices.

2 Answers

By including dimensions in the dependency array for the useEffect function, you are telling React to re-render the page and execute the function in that useEffect block whenever setDimensions is called. Therefore, if you want to prevent that function from being executed when dimensions is updated, you need to remove from the dependencies array, like so

useEffect(async () => {
...
}, [filteredDimensions, dimensionTime])

Since your state updates are fired from an async function, react does not batch them by default (applies to React versions before 18, as in 18 all updates are batched regardless of where they originate).

You need to opt-in to unstable_batchedUpdates, so

const loadCustomReport = async(reportName) => {
   let res = await axios.post('/api/customReport/loadReport', {
      report_name: reportName
   }) // get report setting

   let setting = JSON.parse(res.data[0].report_setting) // parse report setting as json

   React.unstable_batchedUpdates(() => {
      setRevenue(setting.metrics.revenue)
      setImpression(setting.metrics.impression)
      setClicks(setting.metrics.clicks)
      setCPM(setting.metrics.CPM)
      setCTR(setting.metrics.CTR)

      setDimensions(setting.dimensions)
      setFilteredDimensions(setting.filteredDimensions)
   });
}
Related