Source

src/hooks/useLoadData.js

import { useDispatch, useSelector } from 'react-redux'
import { useEffect, useRef } from 'react'
import {
    findIn,
    getDateLists,
    getFetchParams,
    findSecondaryMonth,
    onlyUniqueArray,
    getClosestIndex,
} from '../utils'
import { useGeoda } from '../contexts/Geoda'
import useGetTable from './useGetTable'
import useGetGeojson from './useGetGeojson'
import useBackgroundLoadData from './useBackgroundLoadData'
import dataDateRanges from '../config/dataDateRanges'
import { paramsSelectors, paramsActions } from '../stores/paramsStore'
import { dataSelectors, dataActions } from '../stores/dataStore'
const { selectCanLoadInBackground } = dataSelectors
const { selectDatasets, selectTables } = paramsSelectors
const { setDataParams } = paramsActions
const { setCanLoadInBackground } = dataActions

const dateLists = getDateLists()

/**
 * @typedef {Object} UseLoadDataReturn
 * @property {Object} geojsonData - GeojsonDataset - see
 *   {@link /src/stores/dataStore/types.ts}
 * @property {Object} numeratorData - Dataset - see
 *   {@link /src/stores/dataStore/types.ts}
 * @property {Object} denominatorData - Dataset - see
 *   {@link /src/stores/dataStore/types.ts}
 * @property {number[]} dateIndices - Array of dates loaded in current data
 * @property {boolean} dataReady - Flag to indicate if data is ready to match
 *   current parameters
 * @property {number} currIndex - Date Index of current data
 * @property {boolean} isBackgroundLoading Flag to indicate if background
 *   loading is in progress
 */
/**
 * Hook to load geospatial and tabular data
 *
 * @category Hooks
 * @param {Object} props
 * @param {Object} props.dataParams - See VariableSpec in
 *   {@link /src/stores/paramsStores/type.ts}
 * @param {string} props.currentData - Name of the current geojson data
 * @returns {UseLoadDataReturn}
 */
function useLoadData({ dataParams, currentData }) {
    // pieces of redux state
    const dispatch = useDispatch()
    const { geoda, geodaReady } = useGeoda()
    const canLoadInBackground = useSelector(selectCanLoadInBackground)
    const datasets = useSelector(selectDatasets)
    const tables = useSelector(selectTables)
    const firstLoad = useRef(true)

    // current state data params
    const currDataset = findIn(datasets, 'file', currentData)
    const [nIsTimeSeries, dIsTimeSeries] = [
        dataParams?.nType && dataParams.nType.includes('time'),
        dataParams?.dType && dataParams.dType.includes('time'),
    ]
    const isTimeSeries = nIsTimeSeries || dIsTimeSeries

    const defaultNumeratorParams = getFetchParams({
        dataParams,
        tables,
        currDataset,
        predicate: 'numerator',
        dateList: dateLists['isoDateList'],
    })

    const defaultDenominatorParams = getFetchParams({
        dataParams,
        tables,
        currDataset,
        predicate: 'denominator',
        dateList: dateLists['isoDateList'],
    })

    const currIndex = isTimeSeries
        ? getClosestIndex(
              dataParams.nIndex,
              defaultNumeratorParams.name || ''
          ) || 30
        : null

    const currRangeIndex = currIndex - (dataParams.nRange || dataParams.dRange)
    const currDatesAvailable =
        dataDateRanges[defaultNumeratorParams?.name?.split('.')[0]]
    const latestAvailableDate =
        currDatesAvailable && currDatesAvailable.length
            ? currDatesAvailable.length -
              [...currDatesAvailable].reverse().findIndex((f) => f === 1)
            : null

    const binTimespans = [
        'latest',
        dateLists.isoDateList[
            latestAvailableDate - (dataParams.nRange || dataParams.dRange)
        ]?.slice(0, 7),
        findSecondaryMonth(
            latestAvailableDate - (dataParams.nRange || dataParams.dRange),
            dateLists.isoDateList
        ),
    ]
    const mapTimespans = [currIndex, currRangeIndex]
        .map((index) => [
            !currIndex || latestAvailableDate - index < 30
                ? 'latest'
                : dateLists.isoDateList[index]?.slice(0, 7),
            !currIndex || latestAvailableDate - index < 30
                ? null
                : findSecondaryMonth(index, dateLists.isoDateList),
        ])
        .flat()

    const currTimespans = [...binTimespans, ...mapTimespans]
        .filter((f) => !!f)
        .filter(onlyUniqueArray)

    const [numeratorParams, denominatorParams] = [
        currTimespans.map((timespan) => ({
            ...defaultNumeratorParams,
            timespan,
        })),
        currTimespans.map((timespan) => ({
            ...defaultDenominatorParams,
            timespan,
        })),
    ]

    const [numeratorData, numeratorDataReady] = useGetTable({
        filesToFetch: numeratorParams,
        shouldFetch: true,
        dateLists,
    })

    const [denominatorData, denominatorDataReady] = useGetTable({
        filesToFetch: denominatorParams,
        shouldFetch: true,
        dateLists,
    })
    const [geojsonData, geojsonDataReady] = useGetGeojson({
        geoda,
        geodaReady,
        currDataset,
    })

    const dateIndices = numeratorData ? numeratorData.dates : null

    // First load fix numerator index
    useEffect(() => {
        if (
            firstLoad.current &&
            numeratorData?.dates &&
            numeratorData.dates.slice(-1)[0]
        ) {
            dispatch(
                setDataParams({
                    nIndex: numeratorData.dates.slice(-1)[0],
                })
            )
            firstLoad.current = false
        }
    }, [
        numeratorData &&
            numeratorData?.dates &&
            numeratorData.dates.slice(-1)[0],
    ])

    useEffect(() => {
        dispatch(
            setCanLoadInBackground(
                !!numeratorDataReady &&
                    !!denominatorDataReady &&
                    !!geojsonDataReady
            )
        )
    }, [numeratorDataReady, denominatorDataReady, geojsonDataReady])

    // const {// isBackgroundLoading: adjacentMonthLoading
    // } =
    useBackgroundLoadData({
        currentGeography: currDataset.geography,
        tables: tables.filter(
            ({ name }) =>
                (!defaultNumeratorParams.name ||
                    defaultNumeratorParams.name === name) &&
                (!defaultDenominatorParams.name ||
                    defaultDenominatorParams.name === name)
        ),
        shouldFetch:
            !!numeratorDataReady &&
            !!denominatorDataReady &&
            !!geojsonDataReady,
        currTimespans,
        dateLists,
        numeratorParams,
        denominatorParams,
        adjacentMonths: [
            dateLists.isoDateList[currIndex - 30]?.slice(0, 7),
            dateLists.isoDateList[currIndex + 30]?.slice(0, 7),
        ],
    })

    const { isBackgroundLoading } = useBackgroundLoadData({
        currentGeography: currDataset.geography,
        tables,
        shouldFetch: canLoadInBackground,
        currTimespans,
        dateLists,
        numeratorParams,
        denominatorParams,
        adjacentMonths: [
            dateLists.isoDateList[currIndex - 30]?.slice(0, 7),
            dateLists.isoDateList[currIndex + 30]?.slice(0, 7),
        ],
    })

    return {
        geojsonData,
        numeratorData,
        denominatorData,
        dateIndices,
        dataReady:
            !!numeratorDataReady &&
            !!denominatorDataReady &&
            !!geojsonDataReady,
        currIndex,
        isBackgroundLoading,
    }
}

export default useLoadData