Refactored fetching to use react-query.

This commit is contained in:
Mike Cao 2022-12-28 15:43:22 -08:00
parent 7bbed0e12b
commit c56f02c475
112 changed files with 255 additions and 492 deletions

View file

@ -1,15 +1,17 @@
import React, { useMemo } from 'react';
import { useMemo } from 'react';
import { StatusLight } from 'react-basics';
import { FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import useFetch from 'hooks/useFetch';
import Dot from 'components/common/Dot';
import useApi from 'hooks/useApi';
import styles from './ActiveUsers.module.css';
export default function ActiveUsers({ websiteId, className, value, interval = 60000 }) {
export default function ActiveUsers({ websiteId, className, value, refetchInterval = 60000 }) {
const url = websiteId ? `/websites/${websiteId}/active` : null;
const { data } = useFetch(url, {
interval,
const { get, useQuery } = useApi();
const { data } = useQuery(['websites:active', websiteId], () => get(url), {
refetchInterval,
});
const count = useMemo(() => {
if (websiteId) {
return data?.[0]?.x || 0;
@ -24,7 +26,7 @@ export default function ActiveUsers({ websiteId, className, value, interval = 60
return (
<div className={classNames(styles.container, className)}>
<Dot />
<StatusLight variant="success" />
<div className={styles.text}>
<div>
<FormattedMessage

View file

@ -1,4 +1,4 @@
import React, { useState, useRef, useEffect } from 'react';
import { useState, useRef, useEffect } from 'react';
import classNames from 'classnames';
import ChartJS from 'chart.js';
import Legend from 'components/metrics/Legend';

View file

@ -1,4 +1,3 @@
import React from 'react';
import { FormattedMessage } from 'react-intl';
import FilterLink from 'components/common/FilterLink';
import MetricsTable from 'components/metrics/MetricsTable';

View file

@ -1,5 +1,4 @@
import React from 'react';
import Dot from 'components/common/Dot';
import { StatusLight } from 'react-basics';
import styles from './ChartTooltip.module.css';
import ReactTooltip from 'react-tooltip';
@ -16,7 +15,7 @@ export default function ChartTooltip({ chartId, tooltip }) {
<div className={styles.content}>
<div className={styles.title}>{title}</div>
<div className={styles.metric}>
<Dot color={labelColor} />
<StatusLight color={labelColor} />
{value} {label}
</div>
</div>

View file

@ -1,4 +1,3 @@
import React from 'react';
import MetricsTable from './MetricsTable';
import { percentFilter } from 'lib/filters';
import { useIntl, defineMessages } from 'react-intl';

View file

@ -1,4 +1,4 @@
import React, { useState } from 'react';
import { useState } from 'react';
import { FixedSizeList } from 'react-window';
import { useSpring, animated, config } from 'react-spring';
import classNames from 'classnames';

View file

@ -1,4 +1,3 @@
import React from 'react';
import MetricsTable from './MetricsTable';
import { useIntl, FormattedMessage } from 'react-intl';
import { getDeviceMessage } from 'components/messages';

View file

@ -1,39 +1,37 @@
import React, { useMemo } from 'react';
import { useMemo } from 'react';
import { Loading } from 'react-basics';
import { colord } from 'colord';
import BarChart from './BarChart';
import { getDateArray, getDateLength } from 'lib/date';
import useFetch from 'hooks/useFetch';
import useApi from 'hooks/useApi';
import useDateRange from 'hooks/useDateRange';
import useTimezone from 'hooks/useTimezone';
import usePageQuery from 'hooks/usePageQuery';
import { EVENT_COLORS } from 'lib/constants';
export default function EventsChart({ websiteId, className, token }) {
const { get, useQuery } = useApi();
const [{ startDate, endDate, unit, modified }] = useDateRange(websiteId);
const [timezone] = useTimezone();
const {
query: { url, eventName },
} = usePageQuery();
const { data, loading } = useFetch(
`/websites/${websiteId}/events`,
{
params: {
startAt: +startDate,
endAt: +endDate,
unit,
tz: timezone,
url,
eventName,
token,
},
},
[modified, eventName],
const { data, isLoading } = useQuery(['events', { websiteId, modified, eventName }], () =>
get(`/websites/${websiteId}/events`, {
startAt: +startDate,
endAt: +endDate,
unit,
timezone,
url,
eventName,
token,
}),
);
const datasets = useMemo(() => {
if (!data) return [];
if (loading) return data;
if (isLoading) return data;
const map = data.reduce((obj, { x, t, y }) => {
if (!obj[x]) {
@ -60,7 +58,7 @@ export default function EventsChart({ websiteId, className, token }) {
borderWidth: 1,
};
});
}, [data, loading]);
}, [data, isLoading]);
function handleUpdate(chart) {
chart.data.datasets = datasets;
@ -68,6 +66,10 @@ export default function EventsChart({ websiteId, className, token }) {
chart.update();
}
if (isLoading) {
return <Loading variant="dots" />;
}
if (!data) {
return null;
}
@ -81,7 +83,7 @@ export default function EventsChart({ websiteId, className, token }) {
height={300}
records={getDateLength(startDate, endDate, unit)}
onUpdate={handleUpdate}
loading={loading}
loading={isLoading}
stacked
/>
);

View file

@ -1,4 +1,3 @@
import React from 'react';
import classNames from 'classnames';
import { safeDecodeURI } from 'next-basics';
import { Button } from 'react-basics';

View file

@ -1,4 +1,3 @@
import React from 'react';
import MetricsTable from './MetricsTable';
import { percentFilter } from 'lib/filters';
import { FormattedMessage } from 'react-intl';

View file

@ -1,7 +1,6 @@
import React from 'react';
import { StatusLight } from 'react-basics';
import { colord } from 'colord';
import classNames from 'classnames';
import Dot from 'components/common/Dot';
import useLocale from 'hooks/useLocale';
import useForceUpdate from 'hooks/useForceUpdate';
import styles from './Legend.module.css';
@ -35,7 +34,7 @@ export default function Legend({ chart }) {
className={classNames(styles.label, { [styles.hidden]: hidden })}
onClick={() => handleClick(datasetIndex)}
>
<Dot color={color.alpha(color.alpha() + 0.2).toHex()} />
<StatusLight color={color.alpha(color.alpha() + 0.2).toHex()} />
<span className={locale}>{text}</span>
</div>
);

View file

@ -1,4 +1,3 @@
import React from 'react';
import { useSpring, animated } from 'react-spring';
import { formatNumber } from 'lib/format';
import styles from './MetricCard.module.css';

View file

@ -1,9 +1,9 @@
import React, { useState } from 'react';
import { useState } from 'react';
import { Loading } from 'react-basics';
import { FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import Loading from 'components/common/Loading';
import ErrorMessage from 'components/common/ErrorMessage';
import useFetch from 'hooks/useFetch';
import useApi from 'hooks/useApi';
import useDateRange from 'hooks/useDateRange';
import usePageQuery from 'hooks/usePageQuery';
import { formatShortTime, formatNumber, formatLongNumber } from 'lib/format';
@ -11,6 +11,7 @@ import MetricCard from './MetricCard';
import styles from './MetricsBar.module.css';
export default function MetricsBar({ websiteId, className }) {
const { get, useQuery } = useApi();
const [dateRange] = useDateRange(websiteId);
const { startDate, endDate, modified } = dateRange;
const [format, setFormat] = useState(true);
@ -18,10 +19,10 @@ export default function MetricsBar({ websiteId, className }) {
query: { url, referrer, os, browser, device, country },
} = usePageQuery();
const { data, error, loading } = useFetch(
`/websites/${websiteId}/stats`,
{
params: {
const { data, error, isLoading } = useQuery(
['websites:stats', { websiteId, modified, url, referrer, os, browser, device, country }],
() =>
get(`/websites/${websiteId}/stats`, {
startAt: +startDate,
endAt: +endDate,
url,
@ -30,9 +31,7 @@ export default function MetricsBar({ websiteId, className }) {
browser,
device,
country,
},
},
[modified, url, referrer, os, browser, device, country],
}),
);
const formatFunc = format
@ -54,7 +53,7 @@ export default function MetricsBar({ websiteId, className }) {
return (
<div className={classNames(styles.bar, className)} onClick={handleSetFormat}>
{!data && loading && <Loading />}
{isLoading && <Loading variant="dots" />}
{error && <ErrorMessage />}
{data && !error && (
<>

View file

@ -1,10 +1,10 @@
import React, { useMemo } from 'react';
import { useMemo } from 'react';
import { Loading } from 'react-basics';
import { defineMessages, useIntl } from 'react-intl';
import firstBy from 'thenby';
import classNames from 'classnames';
import Link from 'components/common/Link';
import Loading from 'components/common/Loading';
import useFetch from 'hooks/useFetch';
import useApi from 'hooks/useApi';
import Arrow from 'assets/arrow-right.svg';
import { percentFilter } from 'lib/filters';
import useDateRange from 'hooks/useDateRange';
@ -36,11 +36,15 @@ export default function MetricsTable({
query: { url, referrer, os, browser, device, country },
} = usePageQuery();
const { formatMessage } = useIntl();
const { get, useQuery } = useApi();
const { data, loading, error } = useFetch(
`/websites/${websiteId}/metrics`,
{
params: {
const { data, isLoading, error } = useQuery(
[
'websites:mnetrics',
{ websiteId, type, modified, url, referrer, os, browser, device, country },
],
() =>
get(`/websites/${websiteId}/metrics`, {
type,
startAt: +startDate,
endAt: +endDate,
@ -50,11 +54,8 @@ export default function MetricsTable({
browser,
device,
country,
},
onDataLoad,
delay: delay || DEFAULT_ANIMATION_DURATION,
},
[type, modified, url, referrer, os, browser, device, country],
}),
{ onSuccess: onDataLoad, retryDelay: delay || DEFAULT_ANIMATION_DURATION },
);
const filteredData = useMemo(() => {
@ -73,7 +74,7 @@ export default function MetricsTable({
return (
<div className={classNames(styles.container, className)}>
{!data && loading && <Loading />}
{!data && isLoading && <Loading variant="dots" />}
{error && <ErrorMessage />}
{data && !error && <DataTable {...props} data={filteredData} className={className} />}
<div className={styles.footer}>

View file

@ -1,4 +1,3 @@
import React from 'react';
import MetricsTable from './MetricsTable';
import { FormattedMessage } from 'react-intl';
import FilterLink from 'components/common/FilterLink';

View file

@ -1,4 +1,4 @@
import React, { useState } from 'react';
import { useState } from 'react';
import { useIntl, defineMessage } from 'react-intl';
import FilterLink from 'components/common/FilterLink';
import FilterButtons from 'components/common/FilterButtons';

View file

@ -1,4 +1,3 @@
import React from 'react';
import { useIntl } from 'react-intl';
import { colord } from 'colord';
import CheckVisible from 'components/helpers/CheckVisible';

View file

@ -1,4 +1,4 @@
import React, { useMemo, useRef } from 'react';
import { useMemo, useRef } from 'react';
import { format, parseISO, startOfMinute, subMinutes, isBefore } from 'date-fns';
import PageviewsChart from './PageviewsChart';
import { getDateArray } from 'lib/date';

View file

@ -1,4 +1,4 @@
import React, { useMemo } from 'react';
import { useMemo } from 'react';
import { FormattedMessage } from 'react-intl';
import { differenceInMinutes } from 'date-fns';
import PageHeader from 'components/layout/PageHeader';

View file

@ -1,9 +1,9 @@
import React, { useMemo, useState } from 'react';
import { useMemo, useState } from 'react';
import { StatusLight } from 'react-basics';
import { FormattedMessage, useIntl } from 'react-intl';
import { FixedSizeList } from 'react-window';
import firstBy from 'thenby';
import { Icon } from 'react-basics';
import Dot from 'components/common/Dot';
import FilterButtons from 'components/common/FilterButtons';
import NoData from 'components/common/NoData';
import { getDeviceMessage, labels } from 'components/messages';
@ -149,7 +149,7 @@ export default function RealtimeLog({ data, websites, websiteId }) {
return (
<div className={styles.row} style={style}>
<div>
<Dot color={getColor(row)} />
<StatusLight color={getColor(row)} />
</div>
<div className={styles.time}>{getTime(row)}</div>
<div className={styles.detail}>

View file

@ -1,4 +1,4 @@
import React, { useMemo, useState, useCallback } from 'react';
import { useMemo, useState, useCallback } from 'react';
import { FormattedMessage } from 'react-intl';
import firstBy from 'thenby';
import { percentFilter } from 'lib/filters';

View file

@ -1,4 +1,4 @@
import React, { useState } from 'react';
import { useState } from 'react';
import { useIntl, defineMessages } from 'react-intl';
import MetricsTable from './MetricsTable';
import FilterButtons from 'components/common/FilterButtons';

View file

@ -1,4 +1,3 @@
import React from 'react';
import MetricsTable from './MetricsTable';
import { FormattedMessage } from 'react-intl';

View file

@ -1,5 +1,5 @@
import { useMemo } from 'react';
import { Row, Column } from 'react-basics';
import { Row, Column, Loading } from 'react-basics';
import PageviewsChart from './PageviewsChart';
import MetricsBar from './MetricsBar';
import WebsiteHeader from './WebsiteHeader';
@ -7,12 +7,11 @@ import DateFilter from 'components/common/DateFilter';
import StickyHeader from 'components/helpers/StickyHeader';
import ErrorMessage from 'components/common/ErrorMessage';
import FilterTags from 'components/metrics/FilterTags';
import useFetch from 'hooks/useFetch';
import useApi from 'hooks/useApi';
import useDateRange from 'hooks/useDateRange';
import useTimezone from 'hooks/useTimezone';
import usePageQuery from 'hooks/usePageQuery';
import { getDateArray, getDateLength, getDateRangeValues } from 'lib/date';
import useApi from 'hooks/useApi';
import styles from './WebsiteChart.module.css';
export default function WebsiteChart({
@ -31,26 +30,24 @@ export default function WebsiteChart({
resolve,
query: { url, referrer, os, browser, device, country },
} = usePageQuery();
const { get } = useApi();
const { get, useQuery } = useApi();
const { data, loading, error } = useFetch(
`/websites/${websiteId}/pageviews`,
{
params: {
const { data, isLoading, error } = useQuery(
['websites:pageviews', { websiteId, modified, url, referrer, os, browser, device, country }],
() =>
get(`/websites/${websiteId}/pageviews`, {
startAt: +startDate,
endAt: +endDate,
unit,
tz: timezone,
timezone,
url,
referrer,
os,
browser,
device,
country,
},
onDataLoad,
},
[modified, url, referrer, os, browser, device, country],
}),
{ onSuccess: onDataLoad },
);
const chartData = useMemo(() => {
@ -78,10 +75,13 @@ export default function WebsiteChart({
}
}
if (isLoading) {
return <Loading variant="dots" />;
}
return (
<>
<WebsiteHeader websiteId={websiteId} title={title} domain={domain} />
<StickyHeader
className={styles.metrics}
stickyClassName={styles.sticky}
@ -114,7 +114,7 @@ export default function WebsiteChart({
data={chartData}
unit={unit}
records={getDateLength(startDate, endDate, unit)}
loading={loading}
loading={isLoading}
/>
)}
</Column>