Skip to content

Commit

Permalink
fix: memo table cell for performance
Browse files Browse the repository at this point in the history
  • Loading branch information
Grafikart committed Jan 18, 2024
1 parent bd41cff commit cc25b0f
Show file tree
Hide file tree
Showing 5 changed files with 237 additions and 4 deletions.
1 change: 1 addition & 0 deletions src/components/loop/roster-for-loop/roster-for-loop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ export const RosterForLoop = createCustomizableLunaticField<
<LunaticComponents
blocklist={blockedInLoopComponents}
components={components}
memo
componentProps={(c) => ({
...otherProps,
...c,
Expand Down
13 changes: 12 additions & 1 deletion src/components/lunatic-components.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
Fragment,
isValidElement,
memo,
type PropsWithChildren,
type ReactElement,
type ReactNode,
Expand All @@ -10,6 +11,7 @@ import * as lunaticComponents from './index';
import type { FilledLunaticComponentProps } from '../use-lunatic/commons/fill-components/fill-components';
import { useAutoFocus } from '../hooks/use-auto-focus';
import { hasComponentType } from '../use-lunatic/commons/component';
import { useWhyRender } from '../hooks/use-why-render';

Check warning on line 14 in src/components/lunatic-components.tsx

View workflow job for this annotation

GitHub Actions / test_lint

'useWhyRender' is defined but never used

Check warning on line 14 in src/components/lunatic-components.tsx

View workflow job for this annotation

GitHub Actions / test_lint

'useWhyRender' is defined but never used

type Props<T extends FilledLunaticComponentProps, V = undefined> = {
// List of components to display (coming from getComponents)
Expand All @@ -18,6 +20,8 @@ type Props<T extends FilledLunaticComponentProps, V = undefined> = {
| ReactElement
| { label: string; [key: string]: unknown }
)[];
// Should we memoized children
memo?: boolean;
// Key that trigger autofocus when it changes (pageTag)
autoFocusKey?: string;
// Returns the list of extra props to add to components
Expand All @@ -43,6 +47,7 @@ export function LunaticComponents<
autoFocusKey,
componentProps,
blocklist,
memo,
wrapper = ({ children }) => (
<div className="lunatic lunatic-component">{children}</div>
),
Expand Down Expand Up @@ -81,7 +86,11 @@ export function LunaticComponents<
return (
<Fragment key={computeId(component, k)}>
{wrapper({
children: <LunaticComponent {...props} />,
children: memo ? (
<LunaticComponentMemo {...props} />
) : (
<LunaticComponent {...props} />
),
index: k,
...props,
})}
Expand Down Expand Up @@ -128,6 +137,8 @@ function LunaticComponent(props: ItemProps) {
return <Component {...props} />;
}

const LunaticComponentMemo = memo(LunaticComponent);

function computeId(
component: Record<string, unknown>,
fallback: number | string
Expand Down
26 changes: 26 additions & 0 deletions src/stories/behaviour/performance/performance.stories.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from 'react';
import Orchestrator from '../../utils/orchestrator';
import source from './source.json';
import { generateData } from '../../../../tests/utils/lunatic';
import { times } from '../../../utils/array';

const stories = {
title: 'Behaviour/Performance',
component: Orchestrator,
};

export default stories;

const Template = (args) => <Orchestrator {...args} />;
export const Default = Template.bind({});
Default.args = {
id: 'performance-default',
pagination: true,
source,
data: generateData({
PRENOM: times(200, (k) => `John${k}`),
NOM: times(200, (k) => `Doe${k}`),
AGE: times(200, (k) => k + 1),
BIRTHDAY: times(200, (k) => `2${k.toString().padStart(3, '0')}-01-01`),
}),
};
172 changes: 172 additions & 0 deletions src/stories/behaviour/performance/source.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
{
"maxPage": "3",
"components": [
{
"id": "seq",
"componentType": "Sequence",
"label": {
"value": "\"Description des individus de votre logement\"",
"type": "VTL|MD"
},
"conditionFilter": { "value": "true", "type": "VTL" },
"page": "1"
},
{
"id": "loop-prenom",
"componentType": "RosterForLoop",
"header": [
{ "headerCell": true, "label": "Prénom" },
{ "headerCell": true, "label": "Nom" },
{ "headerCell": true, "label": "Date de naissance" },
{ "headerCell": true, "label": "Age" }
],
"label": { "value": "\"Ajouter un individu\"", "type": "VTL|MD" },
"conditionFilter": { "value": "true", "type": "VTL" },
"bindingDependencies": ["PRENOM", "AGE"],
"lines": {
"min": { "value": 1, "type": "VTL" },
"max": { "value": 10, "type": "VTL" }
},
"page": "1",
"components": [
{
"componentType": "Input",
"conditionFilter": { "value": "true", "type": "VTL" },
"maxLength": 30,
"bindingDependencies": ["PRENOM"],
"id": "prenom",
"response": {
"name": "PRENOM"
}
},
{
"componentType": "Input",
"conditionFilter": { "value": "true", "type": "VTL" },
"maxLength": 30,
"bindingDependencies": ["NOM"],
"id": "nom",
"response": {
"name": "NOM"
}
},
{
"componentType": "Datepicker",
"conditionFilter": { "value": "true", "type": "VTL" },
"maxLength": 30,
"bindingDependencies": ["BIRTHDAY"],
"id": "birthday",
"dateFormat": "YYYY-MM-DD",
"response": {
"name": "BIRTHDAY"
}
},
{
"componentType": "Input",
"conditionFilter": { "value": "true", "type": "VTL" },
"maxLength": 30,
"bindingDependencies": ["AGE"],
"id": "age",
"response": {
"name": "AGE"
}
}
]
},
{
"id": "loop",
"componentType": "Loop",

"loopDependencies": ["PRENOM"],
"iterations": { "value": "count(PRENOM)", "type": "VTL" },
"page": "2",
"maxPage": "1",
"depth": 1,
"paginatedLoop": true,
"conditionFilter": {
"value": "true",
"type": "VTL"
},
"components": [
{
"id": "age",
"label": {
"value": "PRENOM || \", quel est vôtre âge ?\"",
"type": "VTL"
},
"conditionFilter": {
"value": "true",
"type": "VTL"
},
"page": "2.1",
"componentType": "InputNumber",
"min": 0,
"max": 120,
"decimals": 0,
"response": { "name": "AGE" }
}
]
},
{
"id": "seq-end",
"componentType": "Sequence",
"label": {
"value": "\"End\"",
"type": "VTL|MD"
},
"conditionFilter": { "value": "true", "type": "VTL" },
"page": "3"
}
],
"resizing": {
"PRENOM": {
"size": "count(PRENOM)",
"variables": ["AGE"]
}
},
"variables": [
{
"variableType": "COLLECTED",
"name": "PRENOM",
"values": {
"PREVIOUS": [null],
"COLLECTED": [null],
"FORCED": [null],
"EDITED": [null],
"INPUTED": [null]
}
},
{
"variableType": "COLLECTED",
"name": "NOM",
"values": {
"PREVIOUS": [null],
"COLLECTED": [null],
"FORCED": [null],
"EDITED": [null],
"INPUTED": [null]
}
},
{
"variableType": "COLLECTED",
"name": "BIRTHDAY",
"values": {
"PREVIOUS": [null],
"COLLECTED": [null],
"FORCED": [null],
"EDITED": [null],
"INPUTED": [null]
}
},
{
"variableType": "COLLECTED",
"name": "AGE",
"values": {
"PREVIOUS": [null],
"COLLECTED": [null],
"FORCED": [null],
"EDITED": [null],
"INPUTED": [null]
}
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,10 @@ function fillChildComponentsWithIteration(
getComponents: (iteration: number) =>
fillComponents(component.components, {
...state,
handleChange: (response, value) => {
state.handleChange(response, value, { iteration: [iteration] });
},
handleChange: createChangeHandlerForIteration(
state.handleChange,
iteration
),
pager: {
...state.pager,
iteration: iteration,
Expand All @@ -71,6 +72,28 @@ function fillChildComponentsWithIteration(
};
}

// Create change handler memoized for every iteration
let changeHandler = null as null | LunaticState['handleChange'];
const changeHandlerMap = new Map<number, LunaticState['handleChange']>();
function createChangeHandlerForIteration(
handleChange: LunaticState['handleChange'],
iteration: number
) {
if (handleChange !== changeHandler) {
changeHandler = handleChange;
changeHandlerMap.clear();
}
let handler = changeHandlerMap.get(iteration);
if (handler) {
return handler;
}
handler = (response, value) => {
handleChange(response, value, { iteration: [iteration] });
};
changeHandlerMap.set(iteration, handler);
return handler;
}

/**
* For pairwise, inject a method to retrieve component at a specific iteration combination
*/
Expand Down

0 comments on commit cc25b0f

Please sign in to comment.