Skip to content

Commit

Permalink
[Chore] Extra memoization for components to prevent re-rendering (#1988)
Browse files Browse the repository at this point in the history
  • Loading branch information
igorDykhta committed Sep 30, 2022
1 parent 4e88e83 commit 73dba52
Show file tree
Hide file tree
Showing 11 changed files with 169 additions and 117 deletions.
14 changes: 9 additions & 5 deletions src/components/src/common/field-selector.tsx
Expand Up @@ -129,12 +129,16 @@ function FieldSelectorFactory(
};

fieldsSelector = props => props.fields;
filteredFieldsSelector = props =>
props.fields.filter(
field =>
!toArray(props.value).find(d => (d.name ? d.name === field.name : d === field.name))
);
valueSelector = props => props.value;
filteredFieldsSelector = createSelector(
this.fieldsSelector,
this.valueSelector,
(fields, value) => {
return fields.filter(
field => !toArray(value).find(d => (d.name ? d.name === field.name : d === field.name))
);
}
);
filterFieldTypesSelector = props => props.filterFieldTypes;
showTokenSelector = props => props.showToken;

Expand Down
29 changes: 18 additions & 11 deletions src/components/src/common/histogram-plot.tsx
Expand Up @@ -21,8 +21,8 @@
import React, {ReactElement, useMemo} from 'react';
import {scaleLinear} from 'd3-scale';
import {max} from 'd3-array';
import {hcl} from 'd3-color';
import styled from 'styled-components';
import classnames from 'classnames';
import {HistogramBin} from '@kepler.gl/types';

const histogramStyle = {
Expand All @@ -32,16 +32,23 @@ const histogramStyle = {

const HistogramWrapper = styled.svg`
overflow: visible;
.histogram-bars {
rect {
fill: ${props => props.theme.histogramFillOutRange};
}
rect.in-range {
fill: ${props => props.theme.histogramFillInRange};
}
}
`;

type BarType = {
inRange: boolean;
};
const BarUnmemoized = styled.rect<BarType>(
({theme, inRange, color}) => `
${
inRange
? `fill: ${color ?? theme.histogramFillInRange};`
: `fill: ${color ? hcl(color).darker() : theme.histogramFillOutRange};`
}
`
);
const Bar = React.memo(BarUnmemoized);
Bar.displayName = 'Bar';

interface HistogramPlotParams {
width: number;
height: number;
Expand Down Expand Up @@ -99,8 +106,8 @@ function HistogramPlotFactory() {
undefinedToZero(bar.x1) <= value[1] && undefinedToZero(bar.x0) >= value[0];
const wRatio = inRange ? histogramStyle.highlightW : histogramStyle.unHighlightedW;
return (
<rect
className={classnames({'in-range': inRange})}
<Bar
inRange={inRange}
key={bar.x0}
height={y(getValue(bar))}
width={barWidth * wRatio}
Expand Down
7 changes: 5 additions & 2 deletions src/components/src/common/item-selector/item-selector.tsx
Expand Up @@ -155,7 +155,7 @@ export type ItemSelectorProps = {
showDropdownOnMount?: boolean;
};

class ItemSelector extends Component<ItemSelectorProps> {
class ItemSelectorUnmemoized extends Component<ItemSelectorProps> {
static defaultProps = {
multiSelect: true,
placeholder: 'placeholder.enterValue',
Expand Down Expand Up @@ -374,4 +374,7 @@ class ItemSelector extends Component<ItemSelectorProps> {
}
}

export default injectIntl(ItemSelector);
const ItemSelector = React.memo(ItemSelectorUnmemoized);
ItemSelector.displayName = 'ItemSelector';

export default injectIntl(ItemSelectorUnmemoized);
4 changes: 3 additions & 1 deletion src/components/src/editor/editor.tsx
Expand Up @@ -67,7 +67,7 @@ interface EditorProps {
export default function EditorFactory(
FeatureActionPanel: React.FC<FeatureActionPanelProps>
): React.ComponentClass<EditorProps> {
class Editor extends Component<EditorProps> {
class EditorUnmemoized extends Component<EditorProps> {
static defaultProps = {
clickRadius: DEFAULT_RADIUS
};
Expand Down Expand Up @@ -227,5 +227,7 @@ export default function EditorFactory(
}
}

const Editor = (React.memo(EditorUnmemoized) as unknown) as typeof EditorUnmemoized;
Editor.displayName = 'Editor';
return Editor;
}
71 changes: 43 additions & 28 deletions src/components/src/map-container.tsx
Expand Up @@ -19,7 +19,7 @@
// THE SOFTWARE.

// libraries
import React, {Component, createRef} from 'react';
import React, {Component, createRef, useMemo} from 'react';
import MapboxGLMap, {MapRef} from 'react-map-gl';
import DeckGL from '@deck.gl/react';
import {createSelector, Selector} from 'reselect';
Expand Down Expand Up @@ -89,6 +89,8 @@ const MAP_STYLE: {[key: string]: React.CSSProperties} = {
}
};

const LOCALE_CODES_ARRAY = Object.keys(LOCALE_CODES);

const MAPBOXGL_STYLE_UPDATE = 'style.load';
const MAPBOXGL_RENDER = 'render';
const nop = () => {};
Expand All @@ -108,37 +110,50 @@ const MapboxLogo = () => (

export const Attribution = ({showMapboxLogo = true}) => {
const isPalm = hasMobileWidth(breakPointValues);
if (!showMapboxLogo) {

const memoizedComponents = useMemo(() => {
if (!showMapboxLogo) {
return (
<StyledAttrbution>
<a
href="http://www.openstreetmap.org/copyright"
target="_blank"
rel="noopener noreferrer"
>
© OpenStreetMap
</a>
</StyledAttrbution>
);
}

return (
<StyledAttrbution>
<a href="http://www.openstreetmap.org/copyright" target="_blank" rel="noopener noreferrer">
© OpenStreetMap
</a>
{isPalm ? <MapboxLogo /> : null}
<div className="attrition-link">
<a href="https://kepler.gl/policy/" target="_blank" rel="noopener noreferrer">
© kepler.gl |{' '}
</a>
<a href="https://www.mapbox.com/about/maps/" target="_blank" rel="noopener noreferrer">
© Mapbox |{' '}
</a>
<a
href="http://www.openstreetmap.org/copyright"
target="_blank"
rel="noopener noreferrer"
>
© OpenStreetMap |{' '}
</a>
<a href="https://www.mapbox.com/map-feedback/" target="_blank" rel="noopener noreferrer">
<strong>Improve this map </strong>
{!isPalm ? <strong> | </strong> : null}
</a>
{!isPalm ? <MapboxLogo /> : null}
</div>
</StyledAttrbution>
);
}
}, [showMapboxLogo, isPalm]);

return (
<StyledAttrbution>
{isPalm ? <MapboxLogo /> : null}
<div className="attrition-link">
<a href="https://kepler.gl/policy/" target="_blank" rel="noopener noreferrer">
© kepler.gl |{' '}
</a>
<a href="https://www.mapbox.com/about/maps/" target="_blank" rel="noopener noreferrer">
© Mapbox |{' '}
</a>
<a href="http://www.openstreetmap.org/copyright" target="_blank" rel="noopener noreferrer">
© OpenStreetMap |{' '}
</a>
<a href="https://www.mapbox.com/map-feedback/" target="_blank" rel="noopener noreferrer">
<strong>Improve this map </strong>
{!isPalm ? <strong> | </strong> : null}
</a>
{!isPalm ? <MapboxLogo /> : null}
</div>
</StyledAttrbution>
);
return memoizedComponents;
};

MapContainerFactory.deps = [MapPopoverFactory, MapControlFactory, EditorFactory];
Expand Down Expand Up @@ -614,7 +629,7 @@ export default function MapContainerFactory(
<>
<MapControl
datasets={datasets}
availableLocales={Object.keys(LOCALE_CODES)}
availableLocales={LOCALE_CODES_ARRAY}
dragRotate={mapState.dragRotate}
isSplit={isSplit}
primary={primary}
Expand Down
13 changes: 10 additions & 3 deletions src/components/src/notification-panel.tsx
Expand Up @@ -50,8 +50,10 @@ interface NotificationPanelProps {

export default function NotificationPanelFactory(
NotificationItem: ReturnType<typeof NotificationItemFactory>
) {
return class NotificationPanel extends Component<NotificationPanelProps> {
): React.ComponentClass<NotificationPanelProps> {
class NotificationPanelUnmemoized extends Component<NotificationPanelProps> {
static displayName = 'NotificationPanel';

render() {
const globalNotifications = this.props.notifications.filter(
n => n.topic === DEFAULT_NOTIFICATION_TOPICS.global
Expand All @@ -71,5 +73,10 @@ export default function NotificationPanelFactory(
</NotificationPanelContent>
);
}
};
}

const NotificationPanel = (React.memo(
NotificationPanelUnmemoized
) as unknown) as typeof NotificationPanelUnmemoized;
return NotificationPanel;
}
17 changes: 13 additions & 4 deletions src/components/src/side-panel/filter-manager.tsx
Expand Up @@ -75,6 +75,15 @@ function FilterManagerFactory(
.reverse();
}, [filters.length]);

const filterPanelCallbacks = useMemo(() => {
return new Array(filters.length).fill(0).map((d, idx) => ({
removeFilter: () => removeFilter(idx),
enlargeFilter: () => enlargeFilter(idx),
toggleAnimation: () => toggleFilterAnimation(idx),
toggleFilterFeature: () => toggleFilterFeature(idx)
}));
}, [filters.length, removeFilter, enlargeFilter, toggleFilterAnimation, toggleFilterFeature]);

return (
<div className="filter-manager">
<SourceDataCatalog
Expand All @@ -93,10 +102,10 @@ function FilterManagerFactory(
datasets={datasets}
layers={layers}
isAnyFilterAnimating={isAnyFilterAnimating}
removeFilter={() => removeFilter(idx)}
enlargeFilter={() => enlargeFilter(idx)}
toggleAnimation={() => toggleFilterAnimation(idx)}
toggleFilterFeature={() => toggleFilterFeature(idx)}
removeFilter={filterPanelCallbacks[idx].removeFilter}
enlargeFilter={filterPanelCallbacks[idx].enlargeFilter}
toggleAnimation={filterPanelCallbacks[idx].toggleAnimation}
toggleFilterFeature={filterPanelCallbacks[idx].toggleFilterFeature}
setFilter={setFilter}
/>
))}
Expand Down
65 changes: 32 additions & 33 deletions src/components/src/side-panel/layer-manager.tsx
Expand Up @@ -71,39 +71,38 @@ const LayerHeader = styled.div.attrs({
margin-top: 16px;
`;

const LayerBlendingSelector = ({
layerBlending,
updateLayerBlending,
intl
}: LayerBlendingSelectorProps) => {
const labeledLayerBlendings = Object.keys(LAYER_BLENDINGS).reduce(
(acc, current) => ({
...acc,
[intl.formatMessage({id: LAYER_BLENDINGS[current].label})]: current
}),
{}
);

const onChange = useCallback(blending => updateLayerBlending(labeledLayerBlendings[blending]), [
updateLayerBlending,
labeledLayerBlendings
]);

return (
<SidePanelSection>
<PanelLabel>
<FormattedMessage id="layerBlending.title" />
</PanelLabel>
<ItemSelector
selectedItems={intl.formatMessage({id: LAYER_BLENDINGS[layerBlending].label})}
options={Object.keys(labeledLayerBlendings)}
multiSelect={false}
searchable={false}
onChange={onChange}
/>
</SidePanelSection>
);
};
const LayerBlendingSelector = React.memo(
({layerBlending, updateLayerBlending, intl}: LayerBlendingSelectorProps) => {
const labeledLayerBlendings = Object.keys(LAYER_BLENDINGS).reduce(
(acc, current) => ({
...acc,
[intl.formatMessage({id: LAYER_BLENDINGS[current].label})]: current
}),
{}
);

const onChange = useCallback(blending => updateLayerBlending(labeledLayerBlendings[blending]), [
updateLayerBlending,
labeledLayerBlendings
]);

return (
<SidePanelSection>
<PanelLabel>
<FormattedMessage id="layerBlending.title" />
</PanelLabel>
<ItemSelector
selectedItems={intl.formatMessage({id: LAYER_BLENDINGS[layerBlending].label})}
options={Object.keys(labeledLayerBlendings)}
multiSelect={false}
searchable={false}
onChange={onChange}
/>
</SidePanelSection>
);
}
);
LayerBlendingSelector.displayName = 'LayerBlendingSelector';

LayerManagerFactory.deps = [
LayerListFactory,
Expand Down
6 changes: 3 additions & 3 deletions src/components/src/side-panel/layer-panel/dataset-section.tsx
Expand Up @@ -63,7 +63,7 @@ const StyledDatasetSection = styled.div`
`;

export function AddDataButtonFactory() {
const AddDataButton: React.FC<AddDataButtonProps> = ({onClick, isInactive}) => (
const AddDataButton: React.FC<AddDataButtonProps> = React.memo(({onClick, isInactive}) => (
<Button
className="add-data-button"
onClick={onClick}
Expand All @@ -74,8 +74,8 @@ export function AddDataButtonFactory() {
<Add height="12px" />
<FormattedMessage id={'layerManager.addData'} />
</Button>
);

));
AddDataButton.displayName = 'AddDataButton';
return AddDataButton;
}

Expand Down
5 changes: 4 additions & 1 deletion src/components/src/side-panel/panel-header-action.tsx
Expand Up @@ -67,7 +67,7 @@ const HeaderActionWrapper = styled.div<HeaderActionWrapperProps>`
PanelHeaderActionFactory.deps = [];
// Need to use react class to access props.component
export default function PanelHeaderActionFactory(): React.FC<PanelHeaderActionProps> {
const PanelHeaderAction: React.FC<PanelHeaderActionProps> = ({
const PanelHeaderActionUnmemoized: React.FC<PanelHeaderActionProps> = ({
onClick,
tooltip,
id,
Expand Down Expand Up @@ -102,5 +102,8 @@ export default function PanelHeaderActionFactory(): React.FC<PanelHeaderActionPr
</HeaderActionWrapper>
);
};

const PanelHeaderAction = React.memo(PanelHeaderActionUnmemoized);
PanelHeaderAction.displayName = 'PanelHeaderAction';
return PanelHeaderAction;
}

0 comments on commit 73dba52

Please sign in to comment.