Skip to content

Commit

Permalink
perf: limit rerenderings via memoization
Browse files Browse the repository at this point in the history
  • Loading branch information
jsamr committed Jun 4, 2021
1 parent 97dabc1 commit 6512e18
Show file tree
Hide file tree
Showing 15 changed files with 162 additions and 142 deletions.
2 changes: 2 additions & 0 deletions packages/render-html/src/RenderHTML.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import RenderHTMLFragment from './RenderHTMLFragment';
* performance.
*
* @param props - Props for this component.
*
* @public
*/
export default function RenderHTML(props: RenderHTMLProps) {
return (
Expand Down
53 changes: 17 additions & 36 deletions packages/render-html/src/RenderHTMLFragment.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,14 @@
import React, { useMemo } from 'react';
import React from 'react';
import PropTypes from 'prop-types';

import {
RenderResolvedHTMLProps,
ResolvedResourceProps,
RenderHTMLFragmentProps
} from './shared-types';
import useTTree from './hooks/useTTree';
import SharedPropsContext, {
defaultSharedPropsContext
} from './context/SharedPropsContext';
import { ResolvedResourceProps, RenderHTMLFragmentProps } from './shared-types';
import TChildrenRenderersContext from './context/TChildrenRendererContext';
import TNodeChildrenRenderer from './TNodeChildrenRenderer';
import RenderHTMLFragmentDebug from './RenderHTMLFragmentDebug';
import SourceLoader from './SourceLoader';
import TChildrenRenderer from './TChildrenRenderer';
import TDocumentRenderer from './TDocumentRenderer';
import selectSharedProps from './helpers/selectSharedProps';
import RenderResolvedHTML from './RenderResolvedHTML';
import SharedPropsProvider from './context/SharedPropsProvider';
import defaultSharedProps from './context/defaultSharedProps';

export type RenderHTMLFragmentPropTypes = Record<
keyof RenderHTMLFragmentProps,
Expand Down Expand Up @@ -62,26 +54,22 @@ export const renderHtmlFragmentPropTypes: RenderHTMLFragmentPropTypes = {
export const renderHTMLFragmentDefaultProps: {
[k in keyof RenderHTMLFragmentProps]?: RenderHTMLFragmentProps[k];
} = {
...defaultSharedPropsContext,
...defaultSharedProps,
contentWidth: undefined
};

function RenderResolvedHTML(props: RenderResolvedHTMLProps) {
const ttree = useTTree(props);
return (
<TDocumentRenderer
tdoc={ttree}
baseUrl={props.baseUrl}
onDocumentMetadataLoaded={props.onDocumentMetadataLoaded}
/>
);
}
const childrenRendererContext = {
TChildrenRenderer,
TNodeChildrenRenderer
};

/**
* Render a HTML snippet, given that there is a `TRenderEngineProvider` up in
* the render tree.
*
* @param props - Props for this component.
*
* @public
*/
export default function RenderHTMLFragment(props: RenderHTMLFragmentProps) {
const {
Expand All @@ -90,8 +78,7 @@ export default function RenderHTMLFragment(props: RenderHTMLFragmentProps) {
onTTreeChange,
remoteErrorView,
remoteLoadingView,
onDocumentMetadataLoaded,
...remainingProps
onDocumentMetadataLoaded
} = props;
const sourceLoaderProps = {
source,
Expand All @@ -106,20 +93,14 @@ export default function RenderHTMLFragment(props: RenderHTMLFragmentProps) {
/>
)
};

return (
<RenderHTMLFragmentDebug {...props}>
<SharedPropsContext.Provider value={selectSharedProps(remainingProps)}>
<TChildrenRenderersContext.Provider
value={useMemo(
() => ({
TChildrenRenderer,
TNodeChildrenRenderer
}),
[]
)}>
<SharedPropsProvider {...props}>
<TChildrenRenderersContext.Provider value={childrenRendererContext}>
{React.createElement(SourceLoader, sourceLoaderProps)}
</TChildrenRenderersContext.Provider>
</SharedPropsContext.Provider>
</SharedPropsProvider>
</RenderHTMLFragmentDebug>
);
}
Expand Down
15 changes: 15 additions & 0 deletions packages/render-html/src/RenderResolvedHTML.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';
import useTTree from './hooks/useTTree';
import { RenderResolvedHTMLProps } from './shared-types';
import TDocumentRenderer from './TDocumentRenderer';

export default function RenderResolvedHTML(props: RenderResolvedHTMLProps) {
const ttree = useTTree(props);
return (
<TDocumentRenderer
tdoc={ttree}
baseUrl={props.baseUrl}
onDocumentMetadataLoaded={props.onDocumentMetadataLoaded}
/>
);
}
3 changes: 1 addition & 2 deletions packages/render-html/src/TDocumentRenderer.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useMemo } from 'react';
import React, { memo, useEffect, useMemo } from 'react';
import { TDocument } from '@native-html/transient-render-engine';
import { DocumentMetadata, RenderHTMLFragmentProps } from './shared-types';
import DocumentMetadataProvider from './context/DocumentMetadataProvider';
Expand Down Expand Up @@ -52,5 +52,4 @@ const TDocumentRenderer = ({
</DocumentMetadataProvider>
);
};

export default TDocumentRenderer;
2 changes: 1 addition & 1 deletion packages/render-html/src/TNodeChildrenRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
TPhrasing,
TText
} from '@native-html/transient-render-engine';
import { useSharedProps } from './context/SharedPropsContext';
import { useSharedProps } from './context/SharedPropsProvider';
import TChildrenRenderer, {
tchildrenRendererDefaultProps
} from './TChildrenRenderer';
Expand Down
2 changes: 1 addition & 1 deletion packages/render-html/src/TNodeRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import TPhrasingRenderer from './TPhrasingRenderer';
import TTextRenderer from './TTextRenderer';
import { Markers, TNodeRendererProps } from './shared-types';
import { getMarkersFromTNode } from './helpers/getMarkersFromTNode';
import { useSharedProps } from './context/SharedPropsContext';
import { useSharedProps } from './context/SharedPropsProvider';

export type { TNodeRendererProps } from './shared-types';

Expand Down
2 changes: 1 addition & 1 deletion packages/render-html/src/TRenderEngineProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export function useAmbiantTRenderEngine() {

/**
* A react component to share a transient web engine instance across different
* rendered contents via `RenderHTMLFragment`. This can seriously enhance
* rendered contents via `RenderHTMLFragment`. This can significantly enhance
* performance in applications with potentially dozens or hundreds of distinct
* rendered snippets such as chat apps.
*
Expand Down
94 changes: 0 additions & 94 deletions packages/render-html/src/context/SharedPropsContext.ts

This file was deleted.

83 changes: 83 additions & 0 deletions packages/render-html/src/context/SharedPropsProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import React, { PropsWithChildren, useCallback, useMemo } from 'react';
import { TextProps, ViewProps } from 'react-native';
import selectSharedProps from '../helpers/selectSharedProps';
import {
RenderHTMLFragmentProps,
RenderHTMLSharedProps,
TRendererBaseProps
} from '../shared-types';
import defaultSharedProps from './defaultSharedProps';

const SharedPropsContext = React.createContext<Required<RenderHTMLSharedProps>>(
defaultSharedProps
);

export function useSharedProps<
RendererProps extends Record<string, any> = Record<string, any>
>() {
return React.useContext(SharedPropsContext) as Required<
RenderHTMLSharedProps<RendererProps>
>;
}

export function useRendererProps<
RendererProps extends Record<string, any> = Record<string, any>,
K extends keyof RendererProps = any
>(k: K) {
return useSharedProps<RendererProps>().renderersProps[k];
}

export function useDefaultContainerProps(): Pick<
TRendererBaseProps<any>,
'viewProps' | 'textProps'
> {
const sharedProps = useSharedProps();
return {
viewProps: {
...defaultSharedProps.defaultViewProps,
...sharedProps.defaultViewProps
},
textProps: {
...defaultSharedProps.defaultTextProps,
...sharedProps.defaultTextProps
}
};
}
export function useDefaultTextProps(): TextProps {
return {
...defaultSharedProps.defaultTextProps,
...useSharedProps().defaultTextProps
};
}

export function useDefaultViewProps(): ViewProps {
return {
...defaultSharedProps.defaultViewProps,
...useSharedProps().defaultViewProps
};
}

export function useComputeMaxWidthForTag(tagName: string) {
const { computeEmbeddedMaxWidth } = useSharedProps();
return useCallback(
(cw: number) => {
return computeEmbeddedMaxWidth(cw, tagName);
},
[computeEmbeddedMaxWidth, tagName]
);
}

export default function SharedPropsProvider(
props: PropsWithChildren<RenderHTMLFragmentProps>
) {
const memoizedSharedProps = useMemo(
() => selectSharedProps(props),
// eslint-disable-next-line react-hooks/exhaustive-deps
Object.values(selectSharedProps(props))
);
return React.createElement(
SharedPropsContext.Provider,
{ value: memoizedSharedProps },
props.children
);
}
34 changes: 34 additions & 0 deletions packages/render-html/src/context/defaultSharedProps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Dimensions, Linking } from 'react-native';
import { RenderHTMLSharedProps } from '../shared-types';

const defaultSharedProps: Required<RenderHTMLSharedProps> = {
debug: false,
contentWidth: Dimensions.get('window').width,
enableExperimentalPercentWidth: false,
defaultTextProps: {
selectable: false,
allowFontScaling: true
},
defaultViewProps: {},
enableExperimentalMarginCollapsing: false,
computeEmbeddedMaxWidth: (contentWidth) => contentWidth,
imagesInitialDimensions: {
height: 50,
width: 50
},
onLinkPress: (_e, href) => Linking.canOpenURL(href) && Linking.openURL(href),
WebView: () => {
if (__DEV__) {
console.warn(
'One of your renderer is attempting to use WebView component, which has not been ' +
"provided as a prop to the RenderHtml component. As a consequence, the element won't be rendered."
);
}
return null;
},
defaultWebViewProps: {},
renderersProps: {},
setMarkersForTNode: () => null
};

export default defaultSharedProps;
6 changes: 3 additions & 3 deletions packages/render-html/src/helpers/selectSharedProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import pick from 'ramda/src/pick';
import pipe from 'ramda/src/pipe';
import mergeRight from 'ramda/src/mergeRight';
import { RenderHTMLSharedProps, RenderHTMLProps } from '../shared-types';
import { defaultSharedPropsContext } from '../context/SharedPropsContext';
import defaultSharedProps from '../context/defaultSharedProps';

const selectSharedProps: (
props: Partial<RenderHTMLProps>
) => Required<RenderHTMLSharedProps> = pipe(
pick(Object.keys(defaultSharedPropsContext)),
pick(Object.keys(defaultSharedProps)),
pickBy((val) => val != null),
mergeRight(defaultSharedPropsContext) as any
mergeRight(defaultSharedProps) as any
);

export default selectSharedProps;
Loading

0 comments on commit 6512e18

Please sign in to comment.