首页 > 解决方案 > React Custom Hook 产生警告:超过最大更新深度

问题描述

我是 React Hooks 的新手,正在迈出第一步……感谢您的帮助!我想在图表中呈现之前重新使用逻辑来排序和转换数据集。所以我把它分成了一个自定义钩子,但收到了一个警告,它似乎处于重新渲染循环中(慢慢数起来)

Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.

我应该有一个依赖数组,并且依赖项只会在按钮单击时发生变化。所以我不明白为什么它会进入重新渲染循环......?

CigarettesDetailsContainer在 props 中接收“原始”数据并将转换后的数据传递给渲染图表的子组件。它还处理来自孩子的更改日期,因此我将状态保留在这里。

挂钩转换原始数据,并在useSingleValueChartData日期和时间段更改时重新运行。

香烟详情容器

import React, { FC, useState } from 'react'
import moment from 'moment'
import { ApiRegistration } from 'models/Api/ApiRegistration'
import { CigarettesDetails } from './layout'
import { useSingleValueChartData } from 'hooks/useSingleValueChartData'
import { TimePeriod } from 'models/TimePeriod'

interface Props {
    registrations: ApiRegistration[]
}

const initialStart = moment()
    .year(2018)
    .week(5)
    .startOf('isoWeek')
const initialEnd = initialStart.clone().add(1, 'week')
const initialPeriod = TimePeriod.Week

const CigarettesDetailsContainer: FC<Props> = ({ registrations }) => {
    const [startDate, setStartDate] = useState(initialStart)
    const [endDate, setEndDate] = useState(initialEnd)
    const [timePeriod, setTimePeriod] = useState(initialPeriod)

    const data = useSingleValueChartData(
        registrations,
        startDate.toDate(),
        endDate.toDate(),
        timePeriod
    )

    const handleTimeChange = (change: number) => {
        let newStartDate = startDate.clone()
        let newEndDate = endDate.clone()

        switch (timePeriod) {
            default:
                newStartDate.add(change, 'week')
                newEndDate.add(change, 'week')
                break
        }

        setStartDate(newStartDate)
        setEndDate(newEndDate)
    }

    return <CigarettesDetails onTimeChange={handleTimeChange} data={data} />
}

export default CigarettesDetailsContainer

使用单值图表数据

import React, { useEffect, useState } from 'react'
import moment from 'moment'
import { ApiRegistration } from 'models/Api/ApiRegistration'
import { TimePeriod } from 'models/TimePeriod'
import { GroupedChartData, SingleValueChartData } from 'models/ChartData'
import { createWeekdaysList } from 'components/Core/Utils/dateUtils'

export function useSingleValueChartData(
    registrations: ApiRegistration[],
    startDate: Date,
    endDate: Date,
    timePeriod: TimePeriod = TimePeriod.Week
) {
    const [data, setData] = useState<SingleValueChartData[]>([])

    // used for filling chart data set with days without registrations
    let missingWeekDays: string[] = []

    useEffect(() => {
        // which days are missing data
        // eslint-disable-next-line react-hooks/exhaustive-deps
        missingWeekDays = createWeekdaysList(startDate)

        const filteredByDates: ApiRegistration[] = registrations.filter(reg =>
            moment(reg.date).isBetween(startDate, endDate)
        )

        const filteredByDirtyValues = filteredByDates.filter(reg => reg.value && reg.value > -1)

        const grouped: SingleValueChartData[] = Object.values(
            filteredByDirtyValues.reduce(groupByWeekDay, {} as GroupedChartData<
                SingleValueChartData
            >)
        )

        const filled: SingleValueChartData[] = grouped.concat(fillInMissingDays())

        const sorted: SingleValueChartData[] = filled.sort(
            (a: SingleValueChartData, b: SingleValueChartData) =>
                new Date(a.date).getTime() - new Date(b.date).getTime()
        )

        setData(sorted)
    }, [startDate, timePeriod])

    function groupByWeekDay(
        acc: GroupedChartData<SingleValueChartData>,
        { date: dateStr, value }: { date: string; value?: number }
    ): GroupedChartData<SingleValueChartData> {
        const date: string = moment(dateStr).format('YYYY-MM-DD')

        acc[date] = acc[date] || {
            value: 0,
        }

        acc[date] = {
            date,
            value: value ? acc[date].value + value : acc[date].value,
        }

        // remove day from list of missing week days
        const rest = missingWeekDays.filter(d => d !== date)
        missingWeekDays = rest

        return acc
    }

    function fillInMissingDays(): SingleValueChartData[] {
        return missingWeekDays.map(date => {
            return {
                value: 0,
                date,
            }
        })
    }

    return data
}

标签: reactjsreact-hooks

解决方案


在自定义挂钩中,虽然您只想在 startDate 或 timePeriod 更改时运行效果,但目前每次运行效果。

这是因为如何startDateendDate参数被传递给自定义钩子。

const data = useSingleValueChartData(
    registrations,
    startDate.toDate(),
    endDate.toDate(),
    timePeriod
)

.toDate返回新的日期对象。
所以每次新的日期对象都被传递给自定义钩子。

要更正此问题,请将startDateandendDate直接(即不带toDate)传递给自定义挂钩并在自定义挂钩中管理时刻到日期的转换。


推荐阅读