react-vite-demo
react and vite demo
git clone https://9o.is/git/react-vite-demo.git
commit 9cfbd3f1c5f03b3be941b481fd1df6c5636d2190 parent 76c407aad1609b053e3d1410af21f7cfd6f9f221 Author: Jul <jul@9o.is> Date: Thu, 15 Aug 2024 15:59:06 +0800 improve memoization for most components Diffstat:
| M | src/components/headless/Alert.tsx | | | 8 | +++++--- |
| M | src/components/headless/Button.tsx | | | 8 | ++++---- |
| M | src/components/headless/Checkbox.tsx | | | 8 | ++++---- |
| M | src/components/headless/ComboBox.tsx | | | 8 | ++++---- |
| M | src/components/headless/Select.tsx | | | 10 | +++++----- |
| M | src/components/page/EventsPage/EventsPage.tsx | | | 8 | +++++--- |
| M | src/lib/objects.ts | | | 3 | ++- |
7 files changed, 29 insertions(+), 24 deletions(-)
diff --git a/src/components/headless/Alert.tsx b/src/components/headless/Alert.tsx @@ -1,7 +1,9 @@ -export const Alert = ({ error }: { error?: Error }) => { +import { memo } from "react" + +export const Alert = memo(({ error }: { error?: Error }) => { return ( <div role="alert"> {error?.message ?? <> </>} </div> ) -} -\ No newline at end of file +}) +\ No newline at end of file diff --git a/src/components/headless/Button.tsx b/src/components/headless/Button.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from "react" +import { memo, ReactNode } from "react" type ButtonProps = { children: ReactNode @@ -6,6 +6,6 @@ type ButtonProps = { disabled?: boolean } -export function Button({ type = "button", ...props }: ButtonProps) { +export const Button = memo(({ type = "button", ...props }: ButtonProps) => { return <button type={type} {...props} /> -} -\ No newline at end of file +}) +\ No newline at end of file diff --git a/src/components/headless/Checkbox.tsx b/src/components/headless/Checkbox.tsx @@ -1,6 +1,6 @@ -import { useId } from "react" +import { useId, memo } from "react" -export function Checkbox({ label, name }: { label: string, name: string }) { +export const Checkbox = memo(({ label, name }: { label: string, name: string }) => { const id = useId() return ( @@ -9,4 +9,4 @@ export function Checkbox({ label, name }: { label: string, name: string }) { <label htmlFor={id}>{label}</label> </> ) -} -\ No newline at end of file +}) +\ No newline at end of file diff --git a/src/components/headless/ComboBox.tsx b/src/components/headless/ComboBox.tsx @@ -1,4 +1,4 @@ -import { useId } from "react" +import { useId, memo } from "react" export type ComboBoxProps = { label: string @@ -6,7 +6,7 @@ export type ComboBoxProps = { options: string[] } -export function ComboBox({ label, name, options }: ComboBoxProps) { +export const ComboBox = memo(({ label, name, options }: ComboBoxProps) => { const inputId = useId() const datalistId = useId() @@ -19,4 +19,4 @@ export function ComboBox({ label, name, options }: ComboBoxProps) { </datalist> </> ) -} -\ No newline at end of file +}) +\ No newline at end of file diff --git a/src/components/headless/Select.tsx b/src/components/headless/Select.tsx @@ -1,12 +1,12 @@ -import { useId } from "react" +import { useId, memo } from "react" type SelectProps = { label: string name: string - options: string[] + options: readonly string[] } -export function Select({ label, name, options }: SelectProps) { +export const Select = memo(({ label, name, options }: SelectProps) => { const id = useId() return ( @@ -18,4 +18,4 @@ export function Select({ label, name, options }: SelectProps) { </select> </> ) -} -\ No newline at end of file +}) +\ No newline at end of file diff --git a/src/components/page/EventsPage/EventsPage.tsx b/src/components/page/EventsPage/EventsPage.tsx @@ -2,7 +2,8 @@ import { Checkbox, ComboBox, Select, Progress, Alert, Form, FormDataMap, Button import { useNumberFormatter } from '../../hooks' import { useSHEvents } from './useSHEvents' import { FilteredSHEventsInput, useFilteredSHEvents } from './useFilteredSHEvents' -import { invariant, isSortType } from '../../../lib' +import { invariant, isSortType, SORT_TYPES } from '../../../lib' +import { useMemo } from 'react' export function EventsPage() { const { events: loadedEvents, error, loading } = useSHEvents('node-a') @@ -13,14 +14,15 @@ export function EventsPage() { maximumFractionDigits: 0 }) - const cities = [...new Set(loadedEvents.map(({ city }) => city))] + const cities = useMemo(() => + [...new Set(loadedEvents.map(({ city }) => city))], [loadedEvents]) return ( <> <Alert error={error} /> <Form aria-label='Filter Events' onSubmit={filterSHEvents} validator={validator}> <ComboBox label="City" name="city" options={cities} /> - <Select label="Sort by price" name="priceSort" options={['ascending', 'descending']} /> + <Select label="Sort by price" name="priceSort" options={SORT_TYPES} /> <Checkbox label="Find Cheapest" name="cheapest" /> <Button type="submit" disabled={loading}>Search</Button> <Form.Error>{(error) => error}</Form.Error> diff --git a/src/lib/objects.ts b/src/lib/objects.ts @@ -4,7 +4,8 @@ type KeysOfType<T, U> = { type NumberKeysOf<T> = KeysOfType<T, number> -export type SortType = "ascending" | "descending" +export const SORT_TYPES = ["ascending", "descending"] as const +export type SortType = typeof SORT_TYPES[number] export function isSortType(value: string | undefined | null): value is SortType { return value === "ascending" || value === "descending"