Skip to content

Commit 949bbb9

Browse files
committed
UI performance optimizations
1 parent 1e9a833 commit 949bbb9

File tree

9 files changed

+66
-59
lines changed

9 files changed

+66
-59
lines changed

packages/ui/src/components/Autocomplete.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export type AutocompleteItemProps = {
7777
className?: string
7878
};
7979

80-
export function AutocompleteItem({
80+
export const AutocompleteItem = React.memo(function AutocompleteItem({
8181
children,
8282
onClick,
8383
className
@@ -90,4 +90,4 @@ export function AutocompleteItem({
9090
{children}
9191
</div>
9292
)
93-
}
93+
});

packages/ui/src/components/Button.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export type ButtonProps<C extends React.ElementType = "button"> = {
1515
onClick?: React.MouseEventHandler<any>;
1616
} & React.ComponentPropsWithoutRef<C>;
1717

18-
const ButtonInner = React.forwardRef<
18+
const ButtonInner = React.memo(React.forwardRef<
1919
HTMLButtonElement,
2020
ButtonProps<React.ElementType>
2121
>(({
@@ -103,7 +103,7 @@ const ButtonInner = React.forwardRef<
103103
</button>
104104
);
105105

106-
});
106+
}));
107107

108108
ButtonInner.displayName = "Button"
109109

packages/ui/src/components/Checkbox.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ const colorClasses = {
4040
secondary: "bg-secondary"
4141
}
4242

43-
export const Checkbox = ({
43+
export const Checkbox = React.memo(({
4444
id,
4545
checked,
4646
indeterminate = false,
@@ -99,4 +99,4 @@ export const Checkbox = ({
9999
</div>
100100
</CheckboxPrimitive.Root>
101101
);
102-
};
102+
});

packages/ui/src/components/Collapse.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@ interface CollapseProps {
1212
duration?: number;
1313
}
1414

15-
export function Collapse({
15+
export const Collapse = React.memo(({
1616
children,
1717
className,
1818
in: isOpen = false,
1919
duration = 220
20-
}: CollapseProps) {
20+
}: CollapseProps) => {
2121

2222
useInjectStyles(`Collapse-${duration}`, `
2323
.CollapseContent-${duration} {
@@ -61,4 +61,4 @@ export function Collapse({
6161
</Collapsible.Content>
6262
</Collapsible.Root>
6363
)
64-
};
64+
});

packages/ui/src/components/Menu.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,12 @@ export type MenuItemProps = {
6666
className?: string;
6767
};
6868

69-
export function MenuItem({
69+
export const MenuItem = React.memo(({
7070
children,
7171
dense = false, // Default value is false if not provided
7272
onClick,
7373
className
74-
}: MenuItemProps) {
74+
}: MenuItemProps) => {
7575
// Dynamically adjusting the class based on the "dense" prop
7676
const classNames = cls(
7777
onClick && "cursor-pointer",
@@ -87,4 +87,4 @@ export function MenuItem({
8787
{children}
8888
</DropdownMenu.Item>
8989
);
90-
}
90+
});

packages/ui/src/components/MultiSelect.tsx

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ export const MultiSelect = React.forwardRef<
101101
setIsPopoverOpen(open ?? false);
102102
}, [open]);
103103

104-
const allValues = children
104+
const allValues = React.useMemo(() => children
105105
?
106106
// @ts-ignore
107107
Children.map(children, (child) => {
@@ -110,7 +110,18 @@ export const MultiSelect = React.forwardRef<
110110
}
111111
return null;
112112
}).filter(Boolean) as any[]
113-
: [];
113+
: [], [children]);
114+
115+
const optionsMap = React.useMemo(() => {
116+
const map = new Map<string, React.ReactNode>();
117+
Children.forEach(children, (child) => {
118+
if (React.isValidElement(child)) {
119+
// @ts-ignore
120+
map.set(String(child.props.value), child.props.children);
121+
}
122+
});
123+
return map;
124+
}, [children]);
114125

115126
React.useEffect(() => {
116127
setSelectedValues(value ?? []);
@@ -220,24 +231,17 @@ export const MultiSelect = React.forwardRef<
220231
{renderValues && renderValues(selectedValues)}
221232
{!renderValues && selectedValues.map((value) => {
222233

223-
// @ts-ignore
224-
const childrenProps: MultiSelectItemProps[] = Children.map(children, (child) => {
225-
if (React.isValidElement(child)) {
226-
return child.props;
227-
}
228-
}).filter(Boolean);
229-
230-
const option = childrenProps.find((o) => String(o.value) === String(value));
234+
const optionChildren = optionsMap.get(String(value));
231235
if (!useChips) {
232-
return option?.children;
236+
return optionChildren;
233237
}
234238
return (
235239
<Chip
236240
size={"medium"}
237241
key={String(value)}
238242
className={cls("flex flex-row items-center p-1")}
239243
>
240-
{option?.children}
244+
{optionChildren}
241245
<CloseIcon
242246
size={"smallest"}
243247
onClick={(event) => {
@@ -341,7 +345,7 @@ export interface MultiSelectItemProps<T extends MultiSelectValue = string> {
341345
className?: string;
342346
}
343347

344-
export function MultiSelectItem<T extends MultiSelectValue = string>({
348+
export const MultiSelectItem = React.memo(function MultiSelectItem<T extends MultiSelectValue = string>({
345349
children,
346350
value,
347351
className
@@ -378,9 +382,9 @@ export function MultiSelectItem<T extends MultiSelectValue = string>({
378382
<InnerCheckBox checked={isSelected}/>
379383
{children}
380384
</CommandPrimitive.Item>;
381-
}
385+
});
382386

383-
function InnerCheckBox({ checked }: { checked: boolean }) {
387+
const InnerCheckBox = React.memo(function InnerCheckBox({ checked }: { checked: boolean }) {
384388
return <div className={cls(
385389
"p-2",
386390
"w-8 h-8",
@@ -397,5 +401,5 @@ function InnerCheckBox({ checked }: { checked: boolean }) {
397401
{checked && <CheckIcon size={16} className={"absolute"}/>}
398402
</div>
399403
</div>
400-
}
404+
});
401405

packages/ui/src/components/Select.tsx

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,19 @@ export const Select = forwardRef<HTMLDivElement, SelectProps>(({
102102
// Convert non-string values to strings for Radix UI
103103
const stringValue = value !== undefined ? String(value) : undefined;
104104

105+
const selectedChild = React.useMemo(() => {
106+
if (!hasValue || renderValue) return null;
107+
// @ts-ignore
108+
const childrenProps: SelectItemProps[] = Children.map(children, (child) => {
109+
if (React.isValidElement(child)) {
110+
return child.props;
111+
}
112+
}).filter(Boolean);
113+
114+
const option = childrenProps.find((o) => String(o.value) === String(value));
115+
return option?.children;
116+
}, [children, hasValue, renderValue, value]);
117+
105118
return (
106119
<SelectPrimitive.Root
107120
name={name}
@@ -179,17 +192,7 @@ export const Select = forwardRef<HTMLDivElement, SelectProps>(({
179192
className={"w-full"}>
180193
{hasValue && value !== undefined && renderValue ? renderValue(value) : placeholder}
181194
{/*{hasValue && !renderValue && value}*/}
182-
{hasValue && !renderValue && (() => {
183-
// @ts-ignore
184-
const childrenProps: SelectItemProps[] = Children.map(children, (child) => {
185-
if (React.isValidElement(child)) {
186-
return child.props;
187-
}
188-
}).filter(Boolean);
189-
190-
const option = childrenProps.find((o) => String(o.value) === String(value));
191-
return option?.children;
192-
})()}
195+
{hasValue && !renderValue && selectedChild}
193196

194197
</SelectPrimitive.Value>
195198
</div>
@@ -237,7 +240,7 @@ export type SelectItemProps<T extends SelectValue = string> = {
237240
className?: string,
238241
};
239242

240-
export function SelectItem<T extends SelectValue = string>({
243+
export const SelectItem = React.memo(function SelectItem<T extends SelectValue = string>({
241244
value,
242245
children,
243246
disabled,
@@ -267,15 +270,15 @@ export function SelectItem<T extends SelectValue = string>({
267270
<CheckIcon size={16}/>
268271
</div>
269272
</SelectPrimitive.Item>;
270-
}
273+
});
271274

272275
export type SelectGroupProps = {
273276
label: React.ReactNode,
274277
children: React.ReactNode,
275278
className?: string
276279
};
277280

278-
export function SelectGroup({
281+
export const SelectGroup = React.memo(function SelectGroup({
279282
label,
280283
children,
281284
className
@@ -292,4 +295,4 @@ export function SelectGroup({
292295

293296
{children}
294297
</>;
295-
}
298+
});

packages/ui/src/components/Table.tsx

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export type TableProps = {
1010
style?: React.CSSProperties;
1111
} & React.TableHTMLAttributes<HTMLTableElement>;
1212

13-
export const Table = ({
13+
export const Table = React.memo(({
1414
children,
1515
className,
1616
style,
@@ -23,14 +23,14 @@ export const Table = ({
2323
>
2424
{children}
2525
</table>
26-
);
26+
));
2727

2828
export type TableBodyProps = {
2929
children?: React.ReactNode;
3030
className?: string;
3131
} & React.HTMLAttributes<HTMLTableSectionElement>;
3232

33-
export const TableBody = ({
33+
export const TableBody = React.memo(({
3434
children,
3535
className,
3636
...rest
@@ -41,14 +41,14 @@ export const TableBody = ({
4141
>
4242
{children}
4343
</tbody>
44-
);
44+
));
4545

4646
export type TableHeaderProps = {
4747
children?: React.ReactNode;
4848
className?: string;
4949
} & React.HTMLAttributes<HTMLTableSectionElement>;
5050

51-
export const TableHeader = ({
51+
export const TableHeader = React.memo(({
5252
children,
5353
className,
5454
...rest
@@ -65,7 +65,7 @@ export const TableHeader = ({
6565
{children}
6666
</tr>
6767
</thead>
68-
);
68+
));
6969

7070
export type TableRowProps = {
7171
children?: React.ReactNode;
@@ -74,7 +74,7 @@ export type TableRowProps = {
7474
style?: React.CSSProperties;
7575
} & React.HTMLAttributes<HTMLTableRowElement>;
7676

77-
export const TableRow = ({
77+
export const TableRow = React.memo(({
7878
children,
7979
className,
8080
onClick,
@@ -93,7 +93,7 @@ export const TableRow = ({
9393
>
9494
{children}
9595
</tr>
96-
);
96+
));
9797

9898
export type TableCellProps = {
9999
children?: React.ReactNode;
@@ -105,7 +105,7 @@ export type TableCellProps = {
105105
colspan?: number;
106106
} & React.HTMLAttributes<HTMLTableCellElement>;
107107

108-
export const TableCell = ({
108+
export const TableCell = React.memo(({
109109
children,
110110
header = false,
111111
scope = "",
@@ -133,7 +133,7 @@ export const TableCell = ({
133133
{children}
134134
</Tag>
135135
);
136-
};
136+
});
137137

138138
// This is highly experimental and might break in the future
139139
function getParentName(element: HTMLElement | null): string | undefined {

packages/ui/src/components/TextareaAutosize.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ export const TextareaAutosize = React.forwardRef(function TextareaAutosize(
135135
};
136136
}, [maxRows, minRows, props.placeholder]);
137137

138-
const updateState = (prevState: State, newState: State) => {
138+
const updateState = React.useCallback((prevState: State, newState: State) => {
139139
const {
140140
outerHeightStyle,
141141
overflow
@@ -165,7 +165,7 @@ export const TextareaAutosize = React.forwardRef(function TextareaAutosize(
165165
}
166166
}
167167
return prevState;
168-
};
168+
}, []);
169169

170170
const syncHeight = React.useCallback(() => {
171171
const newState = getUpdatedState();
@@ -180,9 +180,9 @@ export const TextareaAutosize = React.forwardRef(function TextareaAutosize(
180180
setState((prevState) => {
181181
return updateState(prevState, newState);
182182
});
183-
}, [getUpdatedState, onResize]);
183+
}, [getUpdatedState, onResize, updateState]);
184184

185-
const syncHeightWithFlushSync = () => {
185+
const syncHeightWithFlushSync = React.useCallback(() => {
186186
const newState = getUpdatedState();
187187

188188
if (isEmpty(newState)) {
@@ -197,7 +197,7 @@ export const TextareaAutosize = React.forwardRef(function TextareaAutosize(
197197
return updateState(prevState, newState);
198198
});
199199
});
200-
};
200+
}, [getUpdatedState, updateState]);
201201

202202
React.useEffect(() => {
203203
const handleResize = debounce(() => {
@@ -232,7 +232,7 @@ export const TextareaAutosize = React.forwardRef(function TextareaAutosize(
232232
resizeObserver.disconnect();
233233
}
234234
};
235-
});
235+
}, [syncHeightWithFlushSync]);
236236

237237
useLayoutEffect(() => {
238238
syncHeight();

0 commit comments

Comments
 (0)