Skip to content

Commit d3ed957

Browse files
committed
perf(webapp): memoize shared context values
1 parent f5cb10d commit d3ed957

7 files changed

Lines changed: 60 additions & 42 deletions

File tree

.oxlintrc.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
"react/jsx-no-target-blank": "error",
5858
"react/jsx-fragments": "error",
5959
"react/self-closing-comp": "error",
60+
"react/jsx-no-constructed-context-values": "error",
6061
"no-lone-blocks": "error",
6162
"typescript/prefer-function-type": "error",
6263
"typescript/prefer-for-of": "error",
@@ -91,6 +92,15 @@
9192
"rules": {
9293
"prefer-object-has-own": "off"
9394
}
95+
},
96+
{
97+
"files": [
98+
"apps/webapp/app/components/primitives/charts/Chart.tsx",
99+
"apps/webapp/app/components/primitives/Timeline.tsx"
100+
],
101+
"rules": {
102+
"react/jsx-no-constructed-context-values": "off"
103+
}
94104
}
95105
]
96106
}

apps/webapp/app/components/SetupCommands.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { CheckIcon, SparklesIcon } from "@heroicons/react/20/solid";
2-
import { createContext, useContext, useRef, useState } from "react";
2+
import { createContext, useContext, useMemo, useRef, useState } from "react";
33
import { useAppOrigin } from "~/hooks/useAppOrigin";
44
import { useProject } from "~/hooks/useProject";
55
import { useTriggerCliTag } from "~/hooks/useTriggerCliTag";
@@ -24,10 +24,13 @@ const PackageManagerContext = createContext<PackageManagerContextType | undefine
2424
export function PackageManagerProvider({ children }: { children: React.ReactNode }) {
2525
const [activePackageManager, setActivePackageManager] = useState("npm");
2626

27+
const contextValue = useMemo(
28+
() => ({ activePackageManager, setActivePackageManager }),
29+
[activePackageManager]
30+
);
31+
2732
return (
28-
<PackageManagerContext.Provider value={{ activePackageManager, setActivePackageManager }}>
29-
{children}
30-
</PackageManagerContext.Provider>
33+
<PackageManagerContext.Provider value={contextValue}>{children}</PackageManagerContext.Provider>
3134
);
3235
}
3336

apps/webapp/app/components/primitives/LocaleProvider.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { ReactNode } from "react";
2-
import { createContext, useContext } from "react";
2+
import { createContext, useContext, useMemo } from "react";
33

44
type LocaleContext = {
55
locales: string[];
@@ -13,7 +13,7 @@ type LocaleContextProviderProps = {
1313
const Context = createContext<LocaleContext | null>(null);
1414

1515
export const LocaleContextProvider = ({ locales, children }: LocaleContextProviderProps) => {
16-
const value = { locales };
16+
const value = useMemo(() => ({ locales }), [locales]);
1717

1818
return <Context.Provider value={value}>{children}</Context.Provider>;
1919
};

apps/webapp/app/components/primitives/OperatingSystemProvider.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { ReactNode } from "react";
2-
import { createContext, useContext } from "react";
2+
import { createContext, useContext, useMemo } from "react";
33

44
export type OperatingSystemPlatform = "mac" | "windows";
55

@@ -18,7 +18,9 @@ export const OperatingSystemContextProvider = ({
1818
platform,
1919
children,
2020
}: OperatingSystemContextProviderProps) => {
21-
return <Context.Provider value={{ platform }}>{children}</Context.Provider>;
21+
const value = useMemo(() => ({ platform }), [platform]);
22+
23+
return <Context.Provider value={value}>{children}</Context.Provider>;
2224
};
2325

2426
const throwIfNoProvider = () => {

apps/webapp/app/components/primitives/SelectedItemsProvider.tsx

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"use client";
22

3-
import { createContext, useCallback, useContext, useReducer } from "react";
3+
import { createContext, useCallback, useContext, useMemo, useReducer } from "react";
44

55
type SelectedItemsContext = {
66
selectedItems: Set<string>;
@@ -60,21 +60,14 @@ export function SelectedItemsProvider({
6060
[state]
6161
);
6262

63+
const contextValue = useMemo(
64+
() => ({ selectedItems: state.items, select, deselect, toggle, deselectAll, has, hasAll }),
65+
[state.items, select, deselect, toggle, deselectAll, has, hasAll]
66+
);
67+
6368
return (
64-
<SelectedItemsContext.Provider
65-
value={{ selectedItems: state.items, select, deselect, toggle, deselectAll, has, hasAll }}
66-
>
67-
{typeof children === "function"
68-
? children({
69-
selectedItems: state.items,
70-
select,
71-
deselect,
72-
toggle,
73-
deselectAll,
74-
has,
75-
hasAll,
76-
})
77-
: children}
69+
<SelectedItemsContext.Provider value={contextValue}>
70+
{typeof children === "function" ? children(contextValue) : children}
7871
</SelectedItemsContext.Provider>
7972
);
8073
}

apps/webapp/app/components/primitives/Table.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
import { ChevronDownIcon, ChevronUpDownIcon, ChevronUpIcon } from "@heroicons/react/20/solid";
22
import { Link } from "@remix-run/react";
33
import { ClipboardCheckIcon, ClipboardIcon } from "lucide-react";
4-
import React, { type ReactNode, createContext, forwardRef, useContext, useState } from "react";
4+
import React, {
5+
type ReactNode,
6+
createContext,
7+
forwardRef,
8+
useContext,
9+
useMemo,
10+
useState,
11+
} from "react";
512
import { useCopy } from "~/hooks/useCopy";
613
import { cn } from "~/utils/cn";
714
import { Popover, PopoverContent, PopoverVerticalEllipseTrigger } from "./Popover";
@@ -84,8 +91,10 @@ export const Table = forwardRef<HTMLTableElement, TableProps & { variant?: Table
8491
},
8592
ref
8693
) => {
94+
const contextValue = useMemo(() => ({ variant }), [variant]);
95+
8796
return (
88-
<TableContext.Provider value={{ variant }}>
97+
<TableContext.Provider value={contextValue}>
8998
<div
9099
className={cn(
91100
"whitespace-nowrap scrollbar-thin scrollbar-track-transparent scrollbar-thumb-surface-control",

apps/webapp/app/components/primitives/charts/DateRangeContext.tsx

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
import React, { createContext, useState, useContext, type ReactNode } from "react";
1+
import React, {
2+
createContext,
3+
useCallback,
4+
useContext,
5+
useMemo,
6+
useState,
7+
type ReactNode,
8+
} from "react";
29

310
type DateRangeContextType = {
411
/** Start date as ISO string (YYYY-MM-DD) or custom format */
@@ -87,28 +94,22 @@ export function DateRangeProvider({
8794
const [startDate, setStartDate] = useState<string>(defaultStartISO);
8895
const [endDate, setEndDate] = useState<string>(defaultEndISO);
8996

90-
const setDateRange = (start: string, end: string) => {
97+
const setDateRange = useCallback((start: string, end: string) => {
9198
setStartDate(start);
9299
setEndDate(end);
93-
};
100+
}, []);
94101

95-
const resetDateRange = () => {
102+
const resetDateRange = useCallback(() => {
96103
setStartDate(defaultStartISO);
97104
setEndDate(defaultEndISO);
98-
};
99-
100-
return (
101-
<DateRangeContext.Provider
102-
value={{
103-
startDate,
104-
endDate,
105-
setDateRange,
106-
resetDateRange,
107-
}}
108-
>
109-
{children}
110-
</DateRangeContext.Provider>
105+
}, [defaultEndISO, defaultStartISO]);
106+
107+
const contextValue = useMemo(
108+
() => ({ startDate, endDate, setDateRange, resetDateRange }),
109+
[startDate, endDate, setDateRange, resetDateRange]
111110
);
111+
112+
return <DateRangeContext.Provider value={contextValue}>{children}</DateRangeContext.Provider>;
112113
}
113114

114115
export function useDateRange(): DateRangeContextType | null {

0 commit comments

Comments
 (0)