React Custom Hook выдает предупреждение: превышена максимальная глубина обновления - PullRequest
0 голосов
/ 03 октября 2019

Я новичок в 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 преобразует необработанные данные и должен перезапускаться при изменении даты и времени.

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

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 голосов
/ 03 октября 2019

В пользовательском хуке, хотя вы хотите запускать эффект только при изменении startDate или timePeriod, в настоящее время эффект запускается каждый раз.

Это потому, что параметры startDate и endDateпередается в пользовательский хук.

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

.toDate возвращает новый объект даты.
Поэтому каждый раз, когда новый объект даты передается в пользовательский хук.

Чтобы исправить это, передайтеstartDate и endDate напрямую (т.е. без toDate) для пользовательского хука и управления преобразованием момента в дату в пользовательском хуке.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...