Skip to content

Commit 98fd749

Browse files
committed
feat: new dev profiler to get feedback on short updates
BREAKING CHANGE: The `triggerTREInvalidationPropNames` has been discontinued. The idea for this prop originated in the premise that many beginners would disregard the issue of passing literal props triggering many re-renders. But I realized it mostly frustrated expectations of newcomers that this library honors React components contract. So I have finally decided to discard the prop, and instead add a lightweight profiler (in dev mode only, of course), which warns the consumer of re-renders happening in a short period of time. You are now responsible for the invalidation of the TRenderEngine, so make sure you memoize the props if the controlling components is expected to re-render often.
1 parent b2431d2 commit 98fd749

21 files changed

+273
-222
lines changed

apps/demo/src/components/UIHtmlDisplayMolecule.tsx

Lines changed: 48 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@ import UIDisplayLoadingAtom from './UIDisplayLoadingAtom';
55
import useOnLinkPress from '../hooks/useOnLinkPress';
66
import { useColorRoles } from '../theme/colorSystem';
77
import { SYSTEM_FONTS } from '../constants';
8+
import { useMemo } from 'react';
9+
10+
function renderRemoteLoadingView() {
11+
return <UIDisplayLoadingAtom />;
12+
}
13+
14+
const defaultTextProps = {
15+
selectable: true
16+
};
817

918
const UIHtmlDisplayMolecule = React.memo(
1019
({
@@ -21,51 +30,57 @@ const UIHtmlDisplayMolecule = React.memo(
2130
onSelectUri
2231
]);
2332
const { surface, softDivider } = useColorRoles();
24-
const baseStyle = {
25-
color: surface.content,
26-
backgroundColor: surface.background,
27-
//@ts-ignore
28-
...renderHtmlProps.baseStyle
29-
};
33+
const baseStyle = useMemo(
34+
() => ({
35+
color: surface.content,
36+
backgroundColor: surface.background,
37+
...renderHtmlProps.baseStyle
38+
}),
39+
[renderHtmlProps.baseStyle, surface.background, surface.content]
40+
);
3041
const sharedProps = {
3142
contentWidth,
3243
...(renderHtmlProps as any),
33-
renderersProps: {
34-
...renderHtmlProps.renderersProps,
35-
a: {
36-
onPress: onLinkPress,
37-
...renderHtmlProps.renderersProps?.a
38-
},
39-
img: {
40-
enableExperimentalPercentWidth: true,
41-
...renderHtmlProps.renderersProps?.img
42-
}
43-
},
44-
defaultTextProps: {
45-
selectable: true
46-
}
47-
};
48-
const mergedTagsStyles = {
49-
...sharedProps.tagsStyles,
50-
hr: {
51-
marginTop: 16,
52-
marginBottom: 16,
53-
...sharedProps.tagsStyles?.hr,
54-
height: 1,
55-
backgroundColor: softDivider
56-
},
57-
html: {}
44+
renderersProps: useMemo(
45+
() => ({
46+
...renderHtmlProps.renderersProps,
47+
a: {
48+
onPress: onLinkPress,
49+
...renderHtmlProps.renderersProps?.a
50+
},
51+
img: {
52+
enableExperimentalPercentWidth: true,
53+
...renderHtmlProps.renderersProps?.img
54+
}
55+
}),
56+
[onLinkPress, renderHtmlProps.renderersProps]
57+
),
58+
defaultTextProps
5859
};
60+
const mergedTagsStyles = useMemo(
61+
() => ({
62+
...sharedProps.tagsStyles,
63+
hr: {
64+
marginTop: 16,
65+
marginBottom: 16,
66+
...sharedProps.tagsStyles?.hr,
67+
height: 1,
68+
backgroundColor: softDivider
69+
},
70+
html: {}
71+
}),
72+
[sharedProps.tagsStyles, softDivider]
73+
);
5974
const renderHtml = (
6075
<RenderHTML
6176
debug={false}
6277
{...sharedProps}
6378
tagsStyles={mergedTagsStyles}
6479
baseStyle={baseStyle}
80+
source={sharedProps.source}
6581
enableUserAgentStyles
6682
systemFonts={SYSTEM_FONTS}
67-
remoteLoadingView={() => <UIDisplayLoadingAtom />}
68-
triggerTREInvalidationPropNames={['baseStyle', 'tagsStyles']}
83+
remoteLoadingView={renderRemoteLoadingView}
6984
/>
7085
);
7186
return <View style={style}>{renderHtml}</View>;

apps/demo/src/components/screens/HomeDrawerScreen/index.tsx

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
resourceRoutesIndex
1919
} from '../../../nav-model';
2020
import imagesMap from '../../../imagesMap';
21+
import { memo } from 'react';
2122

2223
interface ResourceRouteNav extends ResourceRouteDefinition {
2324
component: React.ComponentType<any>;
@@ -51,27 +52,30 @@ const groups: Array<GroupDefinition> = Object.entries(specsByGroups).map(
5152
return {
5253
group: groupName,
5354
groupLabel: groupName,
54-
routes: pages.map<ResourceRouteNav>((page, pageIndex) => ({
55-
header: () => null,
56-
component: function Page() {
57-
const prevPage = pages[pageIndex - 1] || prevGroupLastPage || null;
58-
const nextPage = pages[pageIndex + 1] || nextGroupFirstPage || null;
59-
return (
60-
<ArticleTemplate
61-
title={page.title}
62-
groupLabel={groupName}
63-
description={page.description}
64-
prevPage={prevPage}
65-
nextPage={nextPage}
66-
imageSource={imagesMap[page.id]}>
67-
{React.createElement(page.component)}
68-
</ArticleTemplate>
69-
);
70-
},
71-
iconName: page.iconName as any,
72-
name: `${groupName as PageGroup}-${page.id}` as const,
73-
title: page.title
74-
}))
55+
routes: pages.map<ResourceRouteNav>((page, pageIndex) => {
56+
return {
57+
header: () => null,
58+
component: function Page() {
59+
const Content = memo(page.component);
60+
const prevPage = pages[pageIndex - 1] || prevGroupLastPage || null;
61+
const nextPage = pages[pageIndex + 1] || nextGroupFirstPage || null;
62+
return (
63+
<ArticleTemplate
64+
title={page.title}
65+
groupLabel={groupName}
66+
description={page.description}
67+
prevPage={prevPage}
68+
nextPage={nextPage}
69+
imageSource={imagesMap[page.id]}>
70+
{React.createElement(Content)}
71+
</ArticleTemplate>
72+
);
73+
},
74+
iconName: page.iconName as any,
75+
name: `${groupName as PageGroup}-${page.id}` as const,
76+
title: page.title
77+
};
78+
})
7579
};
7680
}
7781
);

doc-tools/doc-pages/src/pages/PageFAQ.tsx

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -104,15 +104,6 @@ export default function PageFAQ() {
104104
</Section>
105105
</Chapter>
106106
<Chapter title="Troubleshooting">
107-
<Section title="Some props such as styling props don't cause a re-render, what's wrong?">
108-
<Paragraph>
109-
Props for the <RefRenderHTMLExport name="TRenderEngineConfig" />{' '}
110-
component such as styling props are "cold", because a rebuild of the
111-
engine is costly. To circumvent the issue, you can whitelist props
112-
which should be reactive via{' '}
113-
<RefRenderHtmlProp name="triggerTREInvalidationPropNames" /> prop.
114-
</Paragraph>
115-
</Section>
116107
<Section title="Custom font families don't work, what's happening?">
117108
<Paragraph>
118109
You must register fonts available in your app with{' '}

packages/render-html/jest/setup.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
11
global.__DEV__ = true;
22

3+
global.performance = {
4+
now() {
5+
const [seconds, nano] = process.hrtime();
6+
return seconds * 1000000 + nano / 1000;
7+
}
8+
};
9+
310
console.warn = () => {};

packages/render-html/src/RenderHTML.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,12 @@ export default function RenderHTML(props: RenderHTMLProps): ReactElement {
2626
onTTreeChange,
2727
onDocumentMetadataLoaded,
2828
contentWidth,
29-
...config
29+
...otherProps
3030
} = props;
3131
return (
3232
<RenderHTMLDebug {...props}>
33-
<TRenderEngineProvider {...props}>
34-
<RenderHTMLConfigProvider {...config}>
33+
<TRenderEngineProvider {...otherProps}>
34+
<RenderHTMLConfigProvider {...otherProps}>
3535
{React.createElement(RenderHTMLSource, {
3636
source,
3737
onHTMLLoaded,

packages/render-html/src/RenderHTMLConfigProvider.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import sourceLoaderContext, {
1212
} from './context/sourceLoaderContext';
1313
import RenderRegistryProvider from './context/RenderRegistryProvider';
1414
import { useAmbientTRenderEngine } from './TRenderEngineProvider';
15+
import useProfiler from './hooks/useProfiler';
1516

1617
const childrenRendererContext = {
1718
TChildrenRenderer,
@@ -53,13 +54,14 @@ export default function RenderHTMLConfigProvider(
5354
...sharedProps
5455
} = props;
5556
const engine = useAmbientTRenderEngine();
56-
const sourceLoaderConfig = useMemo(
57-
() => ({
57+
const profile = useProfiler({ prop: 'remoteErrorView or remoteLoadingView' });
58+
const sourceLoaderConfig = useMemo(() => {
59+
__DEV__ && profile();
60+
return {
5861
remoteErrorView: remoteErrorView || defaultRenderError,
5962
remoteLoadingView: remoteLoadingView || defaultRenderLoading
60-
}),
61-
[remoteErrorView, remoteLoadingView]
62-
);
63+
};
64+
}, [remoteErrorView, remoteLoadingView, profile]);
6365
return (
6466
<RenderRegistryProvider
6567
renderers={renderers}

packages/render-html/src/RenderHTMLDebug.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ const RenderHTMLDebug = function RenderHTMLDebug(
4242
if ('computeImagesMaxWidth' in props) {
4343
console.warn(debugMessage.outdatedComputeImagesMaxWidth);
4444
}
45+
if ('triggerTREInvalidationPropNames' in props) {
46+
console.warn(debugMessage.outdatedTriggerTREInvalidation);
47+
}
4548
if (Array.isArray(props.allowedStyles)) {
4649
props.allowedStyles.forEach((s) => {
4750
if (s.indexOf('-') > -1) {

packages/render-html/src/RenderHTMLSource.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import SourceLoaderDom from './SourceLoaderDom';
1818
import debugMessage from './debugMessages';
1919
import contentWidthContext from './context/contentWidthContext';
2020
import isDomSource from './helpers/isDomSource';
21+
import useProfiler from './hooks/useProfiler';
2122

2223
export type RenderHTMLSourcePropTypes = Record<
2324
keyof RenderHTMLSourceProps,
@@ -61,6 +62,7 @@ function RawSourceLoader({
6162
...props
6263
}: SourceLoaderProps): ReactElement | null {
6364
if (isEmptySource(source)) {
65+
/* istanbul ignore next */
6466
if (__DEV__) {
6567
console.warn(debugMessage.noSource);
6668
}
@@ -92,13 +94,16 @@ const RenderHTMLSource = memo(
9294
contentWidth,
9395
...props
9496
}: RenderHTMLSourceProps) {
95-
const ttreeEvents: TTreeEvents = useMemo(
96-
() => ({
97+
const profile = useProfiler({
98+
prop: 'onDocumentMetadataLoaded or onTTreeChange'
99+
});
100+
const ttreeEvents: TTreeEvents = useMemo(() => {
101+
__DEV__ && profile();
102+
return {
97103
onDocumentMetadataLoaded,
98104
onTTreeChange
99-
}),
100-
[onDocumentMetadataLoaded, onTTreeChange]
101-
);
105+
};
106+
}, [onDocumentMetadataLoaded, onTTreeChange, profile]);
102107
if (__DEV__) {
103108
if (!(typeof contentWidth === 'number')) {
104109
console.warn(debugMessage.contentWidth);

packages/render-html/src/TRenderEngineProvider.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,7 @@ export const tRenderEngineProviderPropTypes: Record<
3939
setMarkersForTNode: PropTypes.func,
4040
dangerouslyDisableHoisting: PropTypes.bool,
4141
dangerouslyDisableWhitespaceCollapsing: PropTypes.bool,
42-
selectDomRoot: PropTypes.func,
43-
triggerTREInvalidationPropNames: PropTypes.arrayOf(PropTypes.string)
42+
selectDomRoot: PropTypes.func
4443
};
4544

4645
/**
@@ -67,8 +66,7 @@ export const defaultTRenderEngineProviderProps: TRenderEngineConfig = {
6766
enableCSSInlineProcessing: true,
6867
customHTMLElementModels: {},
6968
fallbackFonts: defaultFallbackFonts,
70-
systemFonts: defaultSystemFonts,
71-
triggerTREInvalidationPropNames: []
69+
systemFonts: defaultSystemFonts
7270
};
7371

7472
/**

packages/render-html/src/__tests__/component.render-html-debug.test.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,18 @@ describe('RenderHTMLDebug', () => {
5555
() => {},
5656
'outdatedComputeImagesMaxWidth'
5757
);
58+
createOutdatedPropTest(
59+
'triggerTREInvalidationPropNames',
60+
() => {},
61+
'outdatedTriggerTREInvalidation'
62+
);
5863
it('should warn of allowedStyles items with hyphens', () => {
5964
console.warn = jest.fn();
6065
render(
6166
//@ts-ignore
6267
React.createElement(RenderHTMLDebug, {
6368
//@ts-expect-error
64-
allowedStyles: ['hello-world'],
69+
allowedStyles: ['hello-world', 'color'],
6570
debug: false,
6671
contentWidth: 10
6772
})
@@ -75,7 +80,7 @@ describe('RenderHTMLDebug', () => {
7580
//@ts-ignore
7681
React.createElement(RenderHTMLDebug, {
7782
//@ts-expect-error
78-
ignoredStyles: ['hello-world'],
83+
ignoredStyles: ['hello-world', 'color'],
7984
debug: false,
8085
contentWidth: 10
8186
})

0 commit comments

Comments
 (0)