【问题标题】:React Custom Hook produce warning: Maximum update depth exceededReact Custom Hook 产生警告:超过最大更新深度
【发布时间】:2019-10-03 09:58:08
【问题描述】:

我是 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 挂钩转换原始数据,并在日期和时间段发生更改时重新运行。

CigarettesDetailsContainer

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

使用SingleValueChartData

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
}

【问题讨论】:

    标签: reactjs react-hooks


    【解决方案1】:

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

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

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

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

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

    【讨论】:

    • 谢谢它的工作。但是日期只传递一次,所以只有一次转换为日期。所以我不明白为什么它会触发无限循环?
    猜你喜欢
    • 2020-01-11
    • 2022-10-02
    • 2019-04-03
    • 2019-12-13
    • 2019-05-28
    • 2019-12-03
    • 2023-02-07
    • 1970-01-01
    • 2020-12-31
    相关资源
    最近更新 更多