From 92933c2b61428432e088572629099c9a442b5dd3 Mon Sep 17 00:00:00 2001 From: iCrawl Date: Tue, 23 Aug 2022 19:39:31 +0200 Subject: [PATCH] refactor: memoize everything --- .../src/components/RouterTransition.tsx | 2 +- .../website/src/components/SidebarItems.tsx | 4 +- .../website/src/components/SidebarLayout.tsx | 31 +-- .../src/components/TableOfContentItems.tsx | 65 ++++--- .../website/src/components/tsdoc/TSDoc.tsx | 177 +++++++++--------- 5 files changed, 150 insertions(+), 129 deletions(-) diff --git a/packages/website/src/components/RouterTransition.tsx b/packages/website/src/components/RouterTransition.tsx index 2fa6dd979..8d9a3005a 100644 --- a/packages/website/src/components/RouterTransition.tsx +++ b/packages/website/src/components/RouterTransition.tsx @@ -21,5 +21,5 @@ export function RouterTransition() { // eslint-disable-next-line react-hooks/exhaustive-deps }, [router.asPath]); - return ; + return ; } diff --git a/packages/website/src/components/SidebarItems.tsx b/packages/website/src/components/SidebarItems.tsx index cee8f5f01..b25ed46cc 100644 --- a/packages/website/src/components/SidebarItems.tsx +++ b/packages/website/src/components/SidebarItems.tsx @@ -1,7 +1,7 @@ import { createStyles, Group, Text, NavLink, Box } from '@mantine/core'; import Link from 'next/link'; import { useRouter } from 'next/router'; -import { type Dispatch, type SetStateAction, useEffect, useState } from 'react'; +import { type Dispatch, type SetStateAction, useEffect, useState, useMemo } from 'react'; import { VscSymbolClass, VscSymbolEnum, @@ -101,7 +101,7 @@ export function SidebarItems({ const router = useRouter(); const [asPathWithoutQueryAndAnchor, setAsPathWithoutQueryAndAnchor] = useState(''); const { classes } = useStyles(); - const groupItems = groupMembers(members); + const groupItems = useMemo(() => groupMembers(members), [members]); useEffect(() => { setAsPathWithoutQueryAndAnchor(router.asPath.split('?')[0]?.split('#')[0] ?? ''); diff --git a/packages/website/src/components/SidebarLayout.tsx b/packages/website/src/components/SidebarLayout.tsx index 39360f982..fc63311b7 100644 --- a/packages/website/src/components/SidebarLayout.tsx +++ b/packages/website/src/components/SidebarLayout.tsx @@ -28,7 +28,7 @@ import type { MDXRemoteSerializeResult } from 'next-mdx-remote'; import Image from 'next/future/image'; import Link from 'next/link'; import { useRouter } from 'next/router'; -import { type PropsWithChildren, useState, useEffect } from 'react'; +import { type PropsWithChildren, useState, useEffect, useMemo } from 'react'; import { VscChevronDown, VscGithubInverted, VscPackage, VscVersions } from 'react-icons/vsc'; import { WiDaySunny, WiNightClear } from 'react-icons/wi'; import useSWR from 'swr'; @@ -170,18 +170,25 @@ export function SidebarLayout({ const { classes } = useStyles({ openedLib: openedLibPicker, openedVersion: openedVersionPicker }); - const versionMenuItems = - versions?.map((item) => ( - - {item} - - )) ?? []; + const versionMenuItems = useMemo( + () => + versions?.map((item) => ( + + {item} + + )) ?? [], + [versions], + ); - const breadcrumbs = asPathWithoutQueryAndAnchor.split('/').map((path, idx, original) => ( - - {path} - - )); + const breadcrumbs = useMemo( + () => + asPathWithoutQueryAndAnchor.split('/').map((path, idx, original) => ( + + {path} + + )), + [asPathWithoutQueryAndAnchor], + ); return ( ( - key={prop.name} href={`#${prop.name}`} component="a" className={classes.link}> - - - {prop.name} - - - - )); - - const methodItems = methods.map((member) => { - const key = `${member.name}${member.overloadIndex && member.overloadIndex > 1 ? `:${member.overloadIndex}` : ''}`; - - return ( - key={key} component="a" href={`#${key}`} className={classes.link}> - - - {member.name} - - {member.overloadIndex && member.overloadIndex > 1 ? ( - - {member.overloadIndex} + const propertyItems = useMemo( + () => + properties.map((prop) => ( + key={prop.name} href={`#${prop.name}`} component="a" className={classes.link}> + + + {prop.name} - ) : null} - - - ); - }); + + + )), + [properties], + ); + + const methodItems = useMemo( + () => + methods.map((member) => { + const key = `${member.name}${ + member.overloadIndex && member.overloadIndex > 1 ? `:${member.overloadIndex}` : '' + }`; + + return ( + key={key} component="a" href={`#${key}`} className={classes.link}> + + + {member.name} + + {member.overloadIndex && member.overloadIndex > 1 ? ( + + {member.overloadIndex} + + ) : null} + + + ); + }), + [methods], + ); return ( diff --git a/packages/website/src/components/tsdoc/TSDoc.tsx b/packages/website/src/components/tsdoc/TSDoc.tsx index 97415d4e2..f9dc5336b 100644 --- a/packages/website/src/components/tsdoc/TSDoc.tsx +++ b/packages/website/src/components/tsdoc/TSDoc.tsx @@ -1,7 +1,7 @@ import { Anchor, Box, Code, Text, useMantineColorScheme } from '@mantine/core'; import { DocNodeKind, StandardTags } from '@microsoft/tsdoc'; import Link from 'next/link'; -import { Fragment, type ReactNode } from 'react'; +import { Fragment, useCallback, type ReactNode } from 'react'; import { PrismAsyncLight as SyntaxHighlighter } from 'react-syntax-highlighter'; import { vscDarkPlus, ghcolors } from 'react-syntax-highlighter/dist/cjs/styles/prism'; import { BlockComment } from './BlockComment'; @@ -16,105 +16,108 @@ import type { DocCommentJSON } from '~/DocModel/comment/RootComment'; export function TSDoc({ node }: { node: AnyDocNodeJSON }): JSX.Element { const { colorScheme } = useMantineColorScheme(); - let numberOfExamples = 0; - let exampleIndex = 0; + const createNode = useCallback( + (node: AnyDocNodeJSON, idx?: number): ReactNode => { + let numberOfExamples = 0; + let exampleIndex = 0; - const createNode = (node: AnyDocNodeJSON, idx?: number): ReactNode => { - switch (node.kind) { - case DocNodeKind.PlainText: - return ( - - {(node as DocPlainTextJSON).text} - - ); - case DocNodeKind.Paragraph: - return ( - - {(node as DocNodeContainerJSON).nodes.map((node, idx) => createNode(node, idx))} - - ); - case DocNodeKind.SoftBreak: - return ; - case DocNodeKind.LinkTag: { - const { codeDestination, urlDestination, text } = node as DocLinkTagJSON; - - if (codeDestination) { + switch (node.kind) { + case DocNodeKind.PlainText: return ( - - - {text ?? codeDestination.name} - - + + {(node as DocPlainTextJSON).text} + + ); + case DocNodeKind.Paragraph: + return ( + + {(node as DocNodeContainerJSON).nodes.map((node, idx) => createNode(node, idx))} + + ); + case DocNodeKind.SoftBreak: + return ; + case DocNodeKind.LinkTag: { + const { codeDestination, urlDestination, text } = node as DocLinkTagJSON; + + if (codeDestination) { + return ( + + + {text ?? codeDestination.name} + + + ); + } + + if (urlDestination) { + return ( + + + {text ?? urlDestination} + + + ); + } + + return null; + } + case DocNodeKind.CodeSpan: { + const { code } = node as DocFencedCodeJSON; + return ( + + {code} + ); } - - if (urlDestination) { + case DocNodeKind.FencedCode: { + const { language, code } = node as DocFencedCodeJSON; return ( - - - {text ?? urlDestination} - - + + {code} + ); } + case DocNodeKind.ParamBlock: + case DocNodeKind.Block: { + const { tag } = node as DocBlockJSON; - return null; - } - case DocNodeKind.CodeSpan: { - const { code } = node as DocFencedCodeJSON; - return ( - - {code} - - ); - } - case DocNodeKind.FencedCode: { - const { language, code } = node as DocFencedCodeJSON; - return ( - - {code} - - ); - } - case DocNodeKind.ParamBlock: - case DocNodeKind.Block: { - const { tag } = node as DocBlockJSON; + if (tag.tagName.toUpperCase() === StandardTags.example.tagNameWithUpperCase) { + exampleIndex++; + } - if (tag.tagName.toUpperCase() === StandardTags.example.tagNameWithUpperCase) { - exampleIndex++; + const index = numberOfExamples > 1 ? exampleIndex : undefined; + + return ( + + {(node as DocBlockJSON).content.map((node, idx) => createNode(node, idx))} + + ); } + case DocNodeKind.Comment: { + const comment = node as DocCommentJSON; + // Cheat a bit by finding out how many comments we have beforehand... + numberOfExamples = comment.customBlocks.filter( + (block) => block.tag.tagName.toUpperCase() === StandardTags.example.tagNameWithUpperCase, + ).length; - const index = numberOfExamples > 1 ? exampleIndex : undefined; - - return ( - - {(node as DocBlockJSON).content.map((node, idx) => createNode(node, idx))} - - ); + return {comment.customBlocks.map((node, idx) => createNode(node, idx))}; + } + default: + console.log(`Captured unknown node kind: ${node.kind}`); + break; } - case DocNodeKind.Comment: { - const comment = node as DocCommentJSON; - // Cheat a bit by finding out how many comments we have beforehand... - numberOfExamples = comment.customBlocks.filter( - (block) => block.tag.tagName.toUpperCase() === StandardTags.example.tagNameWithUpperCase, - ).length; - return {comment.customBlocks.map((node, idx) => createNode(node, idx))}; - } - default: - console.log(`Captured unknown node kind: ${node.kind}`); - break; - } - - return null; - }; + return null; + }, + [colorScheme], + ); return (