Skip to content

Commit

Permalink
Merge pull request #8 from knightburton/feat/v3-prepare
Browse files Browse the repository at this point in the history
feat: v3 - memoize the main components
  • Loading branch information
knightburton authored Oct 12, 2023
2 parents 32bbba0 + a75f3c0 commit 73d03bc
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 183 deletions.
90 changes: 46 additions & 44 deletions src/components/Body.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { memo, useMemo } from 'react';
import { SlotComponentProps, VisibilityMatrix } from '../types';
import classnames from '../utils/classnames';
import styles from './styles.less';
Expand Down Expand Up @@ -31,49 +31,51 @@ export type BodyPrivateProps = {
};
};

const Body = ({
numberOfWeeks,
numberOfTodayWeek,
startRenderOnCurrentWeek,
locale,
numberOfRowsPreRender,
startDate,
updateVisibilityMatrix,
visibilityMatrix,
slots,
slotProps,
}: BodyPrivateProps): JSX.Element => {
const RootSlot = slots?.root || 'div';
const rootProps = { ...(slotProps?.root || {}), className: classnames(styles.body, slotProps?.root?.className) };
const Body = memo(
({
numberOfWeeks,
numberOfTodayWeek,
startRenderOnCurrentWeek,
locale,
numberOfRowsPreRender,
startDate,
updateVisibilityMatrix,
visibilityMatrix,
slots,
slotProps,
}: BodyPrivateProps): JSX.Element => {
const RootSlot = useMemo(() => slots?.root || 'div', [slots]);
const rootProps = useMemo(() => ({ ...(slotProps?.root || {}), className: classnames(styles.body, slotProps?.root?.className) }), [slotProps]);

return (
<RootSlot {...rootProps}>
{!!startDate &&
Array.from(Array(numberOfWeeks + 1).keys()).map(numberOfWeek => (
<BodyRow
key={numberOfWeek}
numberOfWeek={numberOfWeek}
numberOfTodayWeek={numberOfTodayWeek}
startRenderOnCurrentWeek={startRenderOnCurrentWeek}
locale={locale}
numberOfRowsPreRender={numberOfRowsPreRender}
startDate={startDate}
updateVisibilityMatrix={updateVisibilityMatrix}
visibilityMatrix={visibilityMatrix}
slots={{
root: slots?.row,
cell: slots?.cell,
cellContent: slots?.cellContent,
}}
slotProps={{
root: slotProps?.row,
cell: slotProps?.cell,
cellContent: slotProps?.cellContent,
}}
/>
))}
</RootSlot>
);
};
return (
<RootSlot {...rootProps}>
{!!startDate &&
Array.from(Array(numberOfWeeks + 1).keys()).map(numberOfWeek => (
<BodyRow
key={numberOfWeek}
numberOfWeek={numberOfWeek}
numberOfTodayWeek={numberOfTodayWeek}
startRenderOnCurrentWeek={startRenderOnCurrentWeek}
locale={locale}
numberOfRowsPreRender={numberOfRowsPreRender}
startDate={startDate}
updateVisibilityMatrix={updateVisibilityMatrix}
visibilityMatrix={visibilityMatrix}
slots={{
root: slots?.row,
cell: slots?.cell,
cellContent: slots?.cellContent,
}}
slotProps={{
root: slotProps?.row,
cell: slotProps?.cell,
cellContent: slotProps?.cellContent,
}}
/>
))}
</RootSlot>
);
},
);

export default Body;
40 changes: 22 additions & 18 deletions src/components/BodyCell.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback, useMemo } from 'react';
import React, { useCallback, useMemo, memo } from 'react';
import { BodyCellData } from '../types';
import classnames from '../utils/classnames';
import styles from './styles.less';
Expand All @@ -21,24 +21,28 @@ export type BodyCellPrivateProps = {
};
};

const BodyCell = ({ data, locale, slots, slotProps }: BodyCellPrivateProps): JSX.Element => {
const [RootSlot, ContentSlot] = useMemo(() => [slots?.root || 'li', slots?.content || BodyCellContent], [slots]);
const { onClick, className, ...restRootProps } = slotProps?.root || {};
const rootProps = useMemo(() => ({ ...restRootProps, className: classnames(styles.body__cell, className) }), [restRootProps, className]);
const contentProps = useMemo(() => ({ ...(slotProps?.content || {}), className: classnames(styles.body__cell__content, slotProps?.content?.className) }), [slotProps]);
const BodyCell = memo(
({ data, locale, slots, slotProps }: BodyCellPrivateProps): JSX.Element => {
const [RootSlot, ContentSlot] = useMemo(() => [slots?.root || 'li', slots?.content || BodyCellContent], [slots]);
const { onClick, className, ...restRootProps } = slotProps?.root || {};
const rootProps = useMemo(() => ({ ...restRootProps, className: classnames(styles.body__cell, className) }), [restRootProps, className]);
const contentProps = useMemo(() => ({ ...(slotProps?.content || {}), className: classnames(styles.body__cell__content, slotProps?.content?.className) }), [
slotProps,
]);

const handleCellClick = useCallback(
(event: React.MouseEventHandler<HTMLLIElement>) => {
if (onClick) onClick(event, data);
},
[onClick, data],
);
const handleCellClick = useCallback(
(event: React.MouseEventHandler<HTMLLIElement>) => {
if (onClick) onClick(event, data);
},
[onClick, data],
);

return (
<RootSlot {...rootProps} onClick={handleCellClick} role="presentation">
<ContentSlot {...contentProps} data={data} locale={locale} />
</RootSlot>
);
};
return (
<RootSlot {...rootProps} onClick={(!!onClick && handleCellClick) || undefined} role="presentation">
<ContentSlot {...contentProps} data={data} locale={locale} />
</RootSlot>
);
},
);

export default BodyCell;
4 changes: 2 additions & 2 deletions src/components/BodyCellContent.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React from 'react';
import React, { memo } from 'react';
import { BodyCellData, SlotComponentProps } from '../types';
import { getBodyCellContent } from '../helpers';

export type BodyCellContentProps = SlotComponentProps<'div', { data: BodyCellData; locale?: string }>;

const BodyCellContent = ({ data, locale, ...rest }: BodyCellContentProps): JSX.Element => <div {...rest}>{getBodyCellContent(data, locale)}</div>;
const BodyCellContent = memo(({ data, locale, ...rest }: BodyCellContentProps): JSX.Element => <div {...rest}>{getBodyCellContent(data, locale)}</div>);

export default BodyCellContent;
104 changes: 53 additions & 51 deletions src/components/BodyRow.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useMemo, useRef, useEffect } from 'react';
import React, { useMemo, useRef, useEffect, memo } from 'react';
import useOnScreen from '../hooks/useOnScreen';
import { getCellAttributes } from '../helpers';
import { VisibilityMatrix, SlotRefComponentProps } from '../types';
Expand Down Expand Up @@ -30,58 +30,60 @@ export type BodyRowPrivateProps = {
};
};

const BodyRow = ({
numberOfWeek,
numberOfTodayWeek,
startRenderOnCurrentWeek,
startDate,
locale,
visibilityMatrix,
updateVisibilityMatrix,
numberOfRowsPreRender,
slots,
slotProps,
}: BodyRowPrivateProps): JSX.Element => {
const ref = useRef<HTMLUListElement>(null);
const shouldScroll = useRef<boolean>(startRenderOnCurrentWeek && numberOfWeek === numberOfTodayWeek);
const isVisible = useOnScreen(ref);
const shouldRender = useMemo(
() =>
visibilityMatrix[numberOfWeek] ||
Array(numberOfRowsPreRender)
.fill(null)
.some((_, idx) => visibilityMatrix[numberOfWeek - idx]),
[visibilityMatrix, numberOfWeek, numberOfRowsPreRender],
);
const data = useMemo(() => {
if (!startDate || !shouldRender) return [];
const dates = Array.from(Array(7).keys()).map(day => getCellAttributes(startDate, numberOfWeek, day, locale));
return dates;
}, [startDate, numberOfWeek, locale, shouldRender]);
const RootSlot = useMemo(() => slots?.root || 'ul', [slots]);
const rootProps = useMemo(() => ({ ...(slotProps?.root || {}), className: classnames(styles.body__row, slotProps?.root?.className) }), [slotProps]);
const BodyRow = memo(
({
numberOfWeek,
numberOfTodayWeek,
startRenderOnCurrentWeek,
startDate,
locale,
visibilityMatrix,
updateVisibilityMatrix,
numberOfRowsPreRender,
slots,
slotProps,
}: BodyRowPrivateProps): JSX.Element => {
const ref = useRef<HTMLUListElement>(null);
const shouldScroll = useRef<boolean>(startRenderOnCurrentWeek && numberOfWeek === numberOfTodayWeek);
const isVisible = useOnScreen(ref);
const shouldRender = useMemo(
() =>
visibilityMatrix[numberOfWeek] ||
Array(numberOfRowsPreRender)
.fill(null)
.some((_, idx) => visibilityMatrix[numberOfWeek - idx]),
[visibilityMatrix, numberOfWeek, numberOfRowsPreRender],
);
const data = useMemo(() => {
if (!startDate || !shouldRender) return [];
const dates = Array.from(Array(7).keys()).map(day => getCellAttributes(startDate, numberOfWeek, day, locale));
return dates;
}, [startDate, numberOfWeek, locale, shouldRender]);
const RootSlot = useMemo(() => slots?.root || 'ul', [slots]);
const rootProps = useMemo(() => ({ ...(slotProps?.root || {}), className: classnames(styles.body__row, slotProps?.root?.className) }), [slotProps]);

useEffect(() => {
if (isVisible && !shouldRender && updateVisibilityMatrix) updateVisibilityMatrix(numberOfWeek);
}, [isVisible, shouldRender, updateVisibilityMatrix, numberOfWeek]);
useEffect(() => {
if (isVisible && !shouldRender && updateVisibilityMatrix) updateVisibilityMatrix(numberOfWeek);
}, [isVisible, shouldRender, updateVisibilityMatrix, numberOfWeek]);

useEffect(() => {
if (ref.current && shouldScroll.current) ref.current.scrollIntoView();
}, []);
useEffect(() => {
if (ref.current && shouldScroll.current) ref.current.scrollIntoView();
}, []);

return (
<RootSlot ref={ref} key={numberOfWeek} {...rootProps}>
{data.map(cell => (
<BodyCell
key={cell.key}
data={cell}
locale={locale}
slots={{ root: slots?.cell, content: slots?.cellContent }}
slotProps={{ root: slotProps?.cell, content: slotProps?.cellContent }}
/>
))}
</RootSlot>
);
};
return (
<RootSlot ref={ref} key={numberOfWeek} {...rootProps}>
{data.map(cell => (
<BodyCell
key={cell.key}
data={cell}
locale={locale}
slots={{ root: slots?.cell, content: slots?.cellContent }}
slotProps={{ root: slotProps?.cell, content: slotProps?.cellContent }}
/>
))}
</RootSlot>
);
},
);

export default BodyRow;
3 changes: 1 addition & 2 deletions src/components/Container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ export type ContainerPrivateProps = {

const Container = memo(
({ children, slots, slotProps }: ContainerPrivateProps): JSX.Element => {
const containerClassName = classnames(styles.calendar, slotProps?.root?.className);
const containerProps = { ...(slotProps?.root || {}), className: containerClassName };
const containerProps = { ...(slotProps?.root || {}), className: classnames(styles.calendar, slotProps?.root?.className) };
const ContainerSlot = slots?.root || 'div';

return <ContainerSlot {...containerProps}>{children}</ContainerSlot>;
Expand Down
26 changes: 14 additions & 12 deletions src/components/Empty.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { memo } from 'react';
import classnames from '../utils/classnames';
import styles from './styles.less';
import { SlotComponentProps } from '../types';
Expand All @@ -14,17 +14,19 @@ export type EmptyPrivateProps = {
};
};

const Empty = ({ slots, slotProps }: EmptyPrivateProps): JSX.Element => {
const rootClassName = classnames(styles.empty, slotProps?.root?.className);
const { label = 'There is no date range to display', ...restProps } = slotProps?.root || {};
const rootProps = { ...restProps, ...(slots?.root ? { label } : {}), className: rootClassName };
const RootSlot = slots?.root || 'div';
const Empty = memo(
({ slots, slotProps }: EmptyPrivateProps): JSX.Element => {
const rootClassName = classnames(styles.empty, slotProps?.root?.className);
const { label = 'There is no date range to display', ...restProps } = slotProps?.root || {};
const rootProps = { ...restProps, ...(slots?.root ? { label } : {}), className: rootClassName };
const RootSlot = slots?.root || 'div';

return (
<RootSlot {...rootProps}>
<p>{label}</p>
</RootSlot>
);
};
return (
<RootSlot {...rootProps}>
<p>{label}</p>
</RootSlot>
);
},
);

export default Empty;
Loading

0 comments on commit 73d03bc

Please sign in to comment.