React Custom Hook产生警告:超过最大更新深度

时间:2019-10-03 09:58:08

标签: reactjs react-hooks

我是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接收道具中的“原始”数据,并将转换后的数据传递给呈现图表的子组件。它还处理来自孩子的更改日期,因此我将其保留在此处。

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

useSingleValueChartData

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
}

1 个答案:

答案 0 :(得分:0)

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

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

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

.toDate返回新的日期对象。
因此,每次将新的日期对象传递给自定义钩子。

要解决此问题,请将startDateendDate直接传递(即不使用toDate)到自定义钩子,并在自定义钩子中管理当前日期转换。