Skip to content

Commit

Permalink
perf: use React.memo to memoize NowMarkerController and SchedulerEntr…
Browse files Browse the repository at this point in the history
…yRenderer
  • Loading branch information
jvllmr committed Oct 7, 2024
1 parent 1c7b2dc commit d29067f
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 37 deletions.
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -103,5 +103,8 @@
"component",
"react"
],
"packageManager": "pnpm@9.12.0"
"packageManager": "pnpm@9.12.0",
"dependencies": {
"fast-deep-equal": "^3.1.3"
}
}
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

51 changes: 28 additions & 23 deletions src/SchedulerBody/NowMarker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,37 @@ import dayjs, { Dayjs } from "dayjs";
import React, { useEffect, useMemo, useState } from "react";
import { UnknownSchedulerController } from "../controller/controller";

export function NowMarkerController({
markerComponent,
distanceCalculator,
}: {
markerComponent: React.FC<NowMarkerProps>;
distanceCalculator: UnknownSchedulerController["calculateDistancePercentage"];
}) {
const Marker = markerComponent;
const [now, setNow] = useState(dayjs());
export const NowMarkerController = React.memo(
({
markerComponent,
distanceCalculator,
}: {
markerComponent: React.FC<NowMarkerProps>;
distanceCalculator: UnknownSchedulerController["calculateDistancePercentage"];
}) => {
const Marker = markerComponent;
const [now, setNow] = useState(dayjs());

const nowLeft = useMemo(() => {
const distance = distanceCalculator(now, "left");
if (!distance) return undefined;
return `${distance}%`;
}, [distanceCalculator, now]);
const nowLeft = useMemo(() => {
const distance = distanceCalculator(now, "left");
if (!distance) return undefined;
return `${distance}%`;
}, [distanceCalculator, now]);

useEffect(() => {
const timeout = setTimeout(() => setNow(dayjs()), 1000);
useEffect(() => {
const timeout = setTimeout(() => setNow(dayjs()), 1000);

return () => {
clearTimeout(timeout);
};
});
if (!nowLeft) return null;
return <Marker left={nowLeft} now={now} />;
}
return () => {
clearTimeout(timeout);
};
});
if (!nowLeft) return null;
return <Marker left={nowLeft} now={now} />;
},
(prev, next) =>
prev.markerComponent == next.markerComponent &&
prev.distanceCalculator == next.distanceCalculator,
);

export interface NowMarkerProps {
left: MantineStyleProps["left"];
Expand Down
8 changes: 6 additions & 2 deletions src/SchedulerBody/SchedulerBody.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Box, Flex, MantineStyleProps, Paper } from "@mantine/core";
import { useWindowVirtualizer } from "@tanstack/react-virtual";
import { Dayjs } from "dayjs";
import React, { useMemo, useRef } from "react";
import React, { useContext, useMemo, useRef } from "react";
import {
SchedulerController,
SchedulerDisplayUnit,
Expand Down Expand Up @@ -71,6 +71,7 @@ function SchedulerBodyRow<TData, TResource>({
momentStyle,
subMomentCount,
dataIdAccessor,
entryComponent,
}: {
data: TData[];
resourcesCount: number;
Expand All @@ -95,7 +96,7 @@ function SchedulerBodyRow<TData, TResource>({
const rowRef = useRef<HTMLDivElement | null>(null);
const controller = useControllerContext();
const getDataId = useStringAccessor(dataIdAccessor);

const resource = useContext<TResource>(resourceContext);
const filteredData = useMemo(
() => data.filter((item) => getDataResourceId(item).includes(resourceId)),
[data, getDataResourceId, resourceId],
Expand Down Expand Up @@ -128,6 +129,8 @@ function SchedulerBodyRow<TData, TResource>({

return (
<SchedulerEntryRenderer
// @ts-expect-error unkown generics
CustomSchedulerEntry={entryComponent}
key={`entry_${entryId}`}
pos="absolute"
data={item}
Expand All @@ -136,6 +139,7 @@ function SchedulerBodyRow<TData, TResource>({
h="80%"
right={`${endDistance}%`}
display={display}
resource={resource}
/>
);
})}
Expand Down
39 changes: 28 additions & 11 deletions src/SchedulerBody/SchedulerEntry/SchedulerEntry.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { MantineStyleProps, Paper, useMantineTheme } from "@mantine/core";
import { useContext } from "react";
import { resourceContext } from "../contexts";
import { schedulerEntryContext } from "./context";

import deepEqual from "fast-deep-equal";
import React from "react";
export interface SchedulerEntryProps<TData, TResource> {
top: NonNullable<MantineStyleProps["top"]>;
left: NonNullable<MantineStyleProps["left"]>;
Expand Down Expand Up @@ -35,11 +33,30 @@ export const DefaultSchedulerEntry: SchedulerEntryComponent<any, any> = ({
);
};

export function SchedulerEntryRenderer<TData, TResource>(
props: Omit<SchedulerEntryProps<TData, TResource>, "resource">,
) {
const CustomSchedulerEntry: SchedulerEntryComponent<TData, TResource> =
useContext(schedulerEntryContext);
const resource = useContext<TResource>(resourceContext);
return <CustomSchedulerEntry {...props} resource={resource} />;
interface SchedulerEntryRendererProps<TData, TResource>
extends SchedulerEntryProps<TData, TResource> {
CustomSchedulerEntry: SchedulerEntryComponent<TData, TResource>;
}

function SchedulerEntryRenderer_<TData, TResource>({
CustomSchedulerEntry,
...props
}: SchedulerEntryRendererProps<TData, TResource>) {
return <CustomSchedulerEntry {...props} />;
}

export const SchedulerEntryRenderer = React.memo(
<TData, TResource>(props: SchedulerEntryRendererProps<TData, TResource>) => (
<SchedulerEntryRenderer_ {...props} />
),
(prev, next) => {
return (
prev.display == next.display &&
prev.left === next.left &&
prev.right === next.right &&
prev.CustomSchedulerEntry == next.CustomSchedulerEntry &&
deepEqual(prev.resource, next.resource) &&
deepEqual(prev.data, next.data)
);
},
);

0 comments on commit d29067f

Please sign in to comment.