Skip to content

Commit 2611fd9

Browse files
chore: memoize FCs
1 parent 964ac8d commit 2611fd9

12 files changed

Lines changed: 684 additions & 588 deletions

File tree

src/components/hv-image/index.tsx

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ import type {
33
HvComponentOnUpdate,
44
HvComponentProps,
55
} from 'hyperview/src/types';
6-
import { Image, ImageProps } from 'react-native';
6+
import React, { useMemo } from 'react';
7+
import { Image } from 'react-native';
78
import { LOCAL_NAME } from 'hyperview/src/types';
8-
import React from 'react';
99
import { addHref } from 'hyperview/src/core/hyper-ref';
1010
import urlParse from 'url-parse';
1111
import { useProps } from 'hyperview/src/services';
@@ -14,18 +14,21 @@ const HvImage = (props: HvComponentProps) => {
1414
// eslint-disable-next-line react/destructuring-assignment
1515
const { element, onUpdate, options, stylesheets } = props;
1616
const { screenUrl, skipHref } = options;
17-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
18-
const imageProps: Record<string, any> = {};
19-
let source = element.getAttribute('source');
20-
if (source) {
21-
source = urlParse(source, screenUrl, true).toString();
22-
imageProps.source = { uri: source };
23-
}
24-
const componentProps = {
25-
...useProps(element, stylesheets, options),
26-
...imageProps,
27-
} as ImageProps;
28-
const component = React.createElement(Image, componentProps);
17+
const baseProps = useProps(element, stylesheets, options);
18+
const source = useMemo(() => element.getAttribute('source'), [element]);
19+
const componentProps = useMemo(() => {
20+
if (!source) {
21+
return baseProps;
22+
}
23+
return {
24+
...baseProps,
25+
source: { uri: urlParse(source, screenUrl, true).toString() },
26+
};
27+
}, [baseProps, screenUrl, source]);
28+
29+
const component = useMemo(() => React.createElement(Image, componentProps), [
30+
componentProps,
31+
]);
2932
return skipHref
3033
? component
3134
: addHref(

src/components/hv-option/index.tsx

Lines changed: 74 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,13 @@ import type {
55
HvComponentOnUpdate,
66
HvComponentProps,
77
} from 'hyperview/src/types';
8-
import React, { useEffect, useRef, useState } from 'react';
8+
import React, {
9+
useCallback,
10+
useEffect,
11+
useMemo,
12+
useRef,
13+
useState,
14+
} from 'react';
915
import { TouchableWithoutFeedback, View } from 'react-native';
1016
import { LOCAL_NAME } from 'hyperview/src/types';
1117
import { createEventHandler } from 'hyperview/src/core/hyper-ref';
@@ -20,44 +26,74 @@ const HvOption = (props: HvComponentProps) => {
2026
const { element, onUpdate, options, stylesheets } = props;
2127
const [pressed, setPressed] = useState(false);
2228
const { onSelect, onToggle } = options;
23-
const value = element.getAttribute('value');
24-
const selected = element.getAttribute('selected') === 'true';
29+
const value = useMemo(() => element.getAttribute('value'), [element]);
30+
const selected = useMemo(() => element.getAttribute('selected') === 'true', [
31+
element,
32+
]);
2533
const prevSelected = useRef(selected);
2634

2735
// Updates options with pressed/selected state, so that child element can render
2836
// using the appropriate modifier styles.
29-
const newOptions = {
30-
...options,
31-
pressed,
32-
pressedSelected: pressed && selected,
33-
selected,
34-
} as const;
37+
const newOptions = useMemo(() => {
38+
return {
39+
...options,
40+
pressed,
41+
pressedSelected: pressed && selected,
42+
selected,
43+
} as const;
44+
}, [options, pressed, selected]);
45+
3546
const componentProps = useProps(element, stylesheets, newOptions);
3647

48+
const flex = useMemo(() => {
49+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
50+
// @ts-ignore TODO: fix this
51+
return componentProps.style?.flex;
52+
}, [
53+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
54+
// @ts-ignore TODO: fix this
55+
componentProps.style?.flex,
56+
]);
57+
58+
const handlePress = useCallback(() => {
59+
if (onSelect) {
60+
// Updates the DOM state, causing this element to re-render as selected.
61+
// Used in select-single context.
62+
onSelect(value);
63+
}
64+
if (onToggle) {
65+
// Updates the DOM state, toggling this element.
66+
// Used in select-multiple context.
67+
onToggle(value);
68+
}
69+
}, [onSelect, onToggle, value]);
70+
71+
const handlePressIn = useCallback(() => setPressed(true), []);
72+
const handlePressOut = useCallback(() => setPressed(false), []);
73+
3774
// Option renders as an outer TouchableWithoutFeedback view and inner view.
3875
// The outer view handles presses, the inner view handles styling.
39-
const outerProps = {
40-
onPress: createEventHandler(() => {
41-
if (onSelect) {
42-
// Updates the DOM state, causing this element to re-render as selected.
43-
// Used in select-single context.
44-
onSelect(value);
45-
}
46-
if (onToggle) {
47-
// Updates the DOM state, toggling this element.
48-
// Used in select-multiple context.
49-
onToggle(value);
50-
}
51-
}, true),
52-
onPressIn: createEventHandler(() => setPressed(true)),
53-
onPressOut: createEventHandler(() => setPressed(false)),
54-
style: {},
55-
};
56-
if (componentProps.style && componentProps.style.flex) {
57-
// Flex is a style that needs to be lifted from the inner component to the outer
58-
// component to ensure proper layout.
59-
outerProps.style = { flex: componentProps.style.flex };
60-
}
76+
const outerProps = useMemo(() => {
77+
return {
78+
onPress: createEventHandler(handlePress, true),
79+
onPressIn: createEventHandler(handlePressIn),
80+
onPressOut: createEventHandler(handlePressOut),
81+
82+
// Flex is a style that needs to be lifted from the inner component to the outer
83+
// component to ensure proper layout.
84+
style: flex ? { flex } : {},
85+
};
86+
}, [flex, handlePress, handlePressIn, handlePressOut]);
87+
88+
// TODO: Replace with <HvChildren>
89+
const children = useMemo(() => {
90+
return Render.renderChildren(
91+
element,
92+
stylesheets,
93+
onUpdate as HvComponentOnUpdate,
94+
newOptions,
95+
);
96+
}, [element, newOptions, onUpdate, stylesheets]);
6197

6298
useEffect(() => {
6399
if (selected && !prevSelected.current) {
@@ -70,20 +106,13 @@ const HvOption = (props: HvComponentProps) => {
70106
prevSelected.current = selected;
71107
}, [element, onUpdate, selected]);
72108

73-
// TODO: Replace with <HvChildren>
74-
return React.createElement(
75-
TouchableWithoutFeedback,
76-
outerProps,
77-
React.createElement(
78-
View,
79-
componentProps,
80-
...Render.renderChildren(
81-
element,
82-
stylesheets,
83-
onUpdate as HvComponentOnUpdate,
84-
newOptions,
85-
),
86-
),
109+
const view = useMemo(() => {
110+
return React.createElement(View, componentProps, ...children);
111+
}, [componentProps, children]);
112+
113+
return useMemo(
114+
() => React.createElement(TouchableWithoutFeedback, outerProps, view),
115+
[outerProps, view],
87116
);
88117
};
89118

0 commit comments

Comments
 (0)