Skip to content

Commit ca72112

Browse files
charlemeceb
and
ceb
authored
refactor(AssetByVariant, IconByVariant): replace useEffect with useMemo for improved performance (#461)
Co-authored-by: ceb <ce.bray@thecodingmachine.com>
1 parent 029bb37 commit ca72112

File tree

2 files changed

+27
-44
lines changed

2 files changed

+27
-44
lines changed

template/src/components/atoms/AssetByVariant/AssetByVariant.tsx

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { ImageProps, ImageSourcePropType } from 'react-native';
22

3-
import { useEffect, useState } from 'react';
3+
import { useMemo } from 'react';
44
import { Image } from 'react-native';
55
import { z } from 'zod';
66

@@ -15,38 +15,35 @@ type Props = {
1515
const images = getAssetsContext('images');
1616

1717
function AssetByVariant({ extension = 'png', path, ...props }: Props) {
18-
const [image, setImage] = useState<ImageSourcePropType>();
1918
const { variant } = useTheme();
2019

21-
useEffect(() => {
22-
try {
23-
const defaultSource = z
24-
.custom<ImageSourcePropType>()
25-
.parse(images(`./${path}.${extension}`));
20+
const image = useMemo(() => {
21+
const getDefaultSource = () =>
22+
z.custom<ImageSourcePropType>().parse(images(`./${path}.${extension}`));
2623

24+
try {
2725
if (variant === 'default') {
28-
setImage(defaultSource);
29-
return;
26+
return getDefaultSource();
3027
}
3128

3229
try {
33-
const fetchedModule = z
30+
return z
3431
.custom<ImageSourcePropType>()
3532
.parse(images(`./${variant}/${path}.${extension}`));
36-
setImage(fetchedModule);
3733
} catch (error) {
3834
// eslint-disable-next-line no-console
3935
console.warn(
4036
`Couldn't load the image: ${path}.${extension} for the variant ${variant}, Fallback to default`,
4137
error,
4238
);
43-
setImage(defaultSource);
39+
return getDefaultSource();
4440
}
4541
} catch (error) {
4642
// eslint-disable-next-line no-console
4743
console.error(`Couldn't load the image: ${path}`, error);
44+
return null;
4845
}
49-
}, [variant, extension, path]);
46+
}, [path, extension, variant]);
5047

5148
return image && <Image source={image} testID="variant-image" {...props} />;
5249
}
Lines changed: 17 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { ReactElement } from 'react';
22
import type { SvgProps } from 'react-native-svg';
33

4-
import { useCallback, useEffect, useState } from 'react';
4+
import { useMemo } from 'react';
55
import { z } from 'zod';
66

77
import { useTheme } from '@/theme';
@@ -15,60 +15,46 @@ const icons = getAssetsContext('icons');
1515
const EXTENSION = 'svg';
1616

1717
function IconByVariant({ height = 24, path, width = 24, ...props }: Props) {
18-
const [icon, setIcon] = useState<ReactElement<SvgProps>>();
1918
const { variant } = useTheme();
2019

21-
useEffect(() => {
20+
const iconProps = { ...props, height, width };
21+
const Icon = useMemo(() => {
2222
try {
23-
const defaultSource = z
24-
.object({ default: z.custom<ReactElement<SvgProps>>() })
25-
.parse(icons(`./${path}.${EXTENSION}`));
23+
const getDefaultSource = () =>
24+
z
25+
.object({
26+
default: z.function().returns(z.custom<ReactElement<SvgProps>>()),
27+
})
28+
.parse(icons(`./${path}.${EXTENSION}`)).default;
2629

2730
if (variant === 'default') {
28-
setIcon(defaultSource.default);
29-
return;
31+
return getDefaultSource();
3032
}
3133

3234
try {
3335
const fetchedModule = z
34-
.object({ default: z.custom<ReactElement<SvgProps>>() })
36+
.object({
37+
default: z.function().returns(z.custom<ReactElement<SvgProps>>()),
38+
})
3539
.parse(icons(`./${variant}/${path}.${EXTENSION}`));
3640

37-
setIcon(fetchedModule.default);
41+
return fetchedModule.default;
3842
} catch (error) {
3943
// eslint-disable-next-line no-console
4044
console.warn(
4145
`Couldn't load the icon: ${path}.${EXTENSION} for the variant ${variant}, Fallback to default`,
4246
error,
4347
);
44-
setIcon(defaultSource.default);
48+
return getDefaultSource();
4549
}
4650
} catch (error) {
4751
// eslint-disable-next-line no-console
4852
console.error(`Couldn't load the icon: ${path}.${EXTENSION}`, error);
53+
throw error;
4954
}
5055
}, [variant, path]);
5156

52-
const Component = useCallback(
53-
(currentProps: SvgProps) => {
54-
if (!icon) {
55-
return null;
56-
}
57-
58-
return {
59-
...icon,
60-
props: {
61-
...icon.props,
62-
height,
63-
width,
64-
...currentProps,
65-
},
66-
};
67-
},
68-
[icon, width, height],
69-
);
70-
71-
return <Component {...props} />;
57+
return <Icon {...iconProps} />;
7258
}
7359

7460
export default IconByVariant;

0 commit comments

Comments
 (0)