prevent unnecessary re-rendering of rountangles and diamonds
This commit is contained in:
parent
0fc3775a11
commit
2ca2ba5d1b
5 changed files with 45 additions and 12 deletions
|
|
@ -23,3 +23,18 @@ export function memoize<InType,OutType>(fn: (i: InType) => OutType) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// compare arrays by value
|
||||||
|
export function arraysEqual<T>(a: T[], b: T[], cmp: (a: T, b: T) => boolean = (a,b)=>a===b): boolean {
|
||||||
|
if (a === b)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (a.length !== b.length)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (let i=0; i<a.length; i++)
|
||||||
|
if (!cmp(a[i],b[i]))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { rountangleMinSize } from "./VisualEditor";
|
||||||
import { Vec2D } from "./geometry";
|
import { Vec2D } from "./geometry";
|
||||||
import { RectHelper } from "./RectHelpers";
|
import { RectHelper } from "./RectHelpers";
|
||||||
import { memo } from "react";
|
import { memo } from "react";
|
||||||
|
import { arraysEqual } from "@/App/util";
|
||||||
|
|
||||||
export const DiamondShape = memo(function DiamondShape(props: {size: Vec2D, extraAttrs: object}) {
|
export const DiamondShape = memo(function DiamondShape(props: {size: Vec2D, extraAttrs: object}) {
|
||||||
const minSize = rountangleMinSize(props.size);
|
const minSize = rountangleMinSize(props.size);
|
||||||
|
|
@ -20,12 +21,13 @@ export const DiamondShape = memo(function DiamondShape(props: {size: Vec2D, extr
|
||||||
/>;
|
/>;
|
||||||
});
|
});
|
||||||
|
|
||||||
export const DiamondSVG = memo(function DiamondSVG(props: { diamond: Diamond; selected: string[]; highlight: RountanglePart[]; errors: string[]; active: boolean; }) {
|
export const DiamondSVG = memo(function DiamondSVG(props: { diamond: Diamond; selected: RountanglePart[]; highlight: RountanglePart[]; error?: string; active: boolean; }) {
|
||||||
|
console.log('render diamond', props.diamond.uid);
|
||||||
const minSize = rountangleMinSize(props.diamond.size);
|
const minSize = rountangleMinSize(props.diamond.size);
|
||||||
const extraAttrs = {
|
const extraAttrs = {
|
||||||
className: ''
|
className: ''
|
||||||
+ (props.selected.length === 4 ? " selected" : "")
|
+ (props.selected.length === 4 ? " selected" : "")
|
||||||
+ (props.errors.length > 0 ? " error" : "")
|
+ (props.error ? " error" : "")
|
||||||
+ (props.active ? " active" : ""),
|
+ (props.active ? " active" : ""),
|
||||||
"data-uid": props.diamond.uid,
|
"data-uid": props.diamond.uid,
|
||||||
"data-parts": "left top right bottom",
|
"data-parts": "left top right bottom",
|
||||||
|
|
@ -39,4 +41,10 @@ export const DiamondSVG = memo(function DiamondSVG(props: { diamond: Diamond; se
|
||||||
|
|
||||||
<RectHelper uid={props.diamond.uid} size={minSize} highlight={props.highlight} selected={props.selected} />
|
<RectHelper uid={props.diamond.uid} size={minSize} highlight={props.highlight} selected={props.selected} />
|
||||||
</g>;
|
</g>;
|
||||||
|
}, (prevProps, nextProps) => {
|
||||||
|
return prevProps.diamond === nextProps.diamond
|
||||||
|
&& arraysEqual(prevProps.selected, nextProps.selected)
|
||||||
|
&& arraysEqual(prevProps.highlight, nextProps.highlight)
|
||||||
|
&& prevProps.error === nextProps.error
|
||||||
|
&& prevProps.active === nextProps.active
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,8 @@ function lineGeometryProps(size: Vec2D): [RountanglePart, object][] {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const RectHelper = memo(function RectHelper(props: { uid: string, size: Vec2D, selected: string[], highlight: RountanglePart[] }) {
|
// no need to memo() this component, the parent component is already memoized
|
||||||
|
export const RectHelper = function RectHelper(props: { uid: string, size: Vec2D, selected: RountanglePart[], highlight: string[] }) {
|
||||||
const geomProps = lineGeometryProps(props.size);
|
const geomProps = lineGeometryProps(props.size);
|
||||||
return <>
|
return <>
|
||||||
{geomProps.map(([side, ps]) => <g key={side}>
|
{geomProps.map(([side, ps]) => <g key={side}>
|
||||||
|
|
@ -54,4 +55,4 @@ export const RectHelper = memo(function RectHelper(props: { uid: string, size: V
|
||||||
data-uid={props.uid}
|
data-uid={props.uid}
|
||||||
data-parts="bottom left" />
|
data-parts="bottom left" />
|
||||||
</>;
|
</>;
|
||||||
});
|
};
|
||||||
|
|
@ -3,9 +3,12 @@ import { Rountangle, RountanglePart } from "../statecharts/concrete_syntax";
|
||||||
import { ROUNTANGLE_RADIUS } from "./parameters";
|
import { ROUNTANGLE_RADIUS } from "./parameters";
|
||||||
import { RectHelper } from "./RectHelpers";
|
import { RectHelper } from "./RectHelpers";
|
||||||
import { rountangleMinSize } from "./VisualEditor";
|
import { rountangleMinSize } from "./VisualEditor";
|
||||||
|
import { arraysEqual } from "@/App/util";
|
||||||
|
|
||||||
|
|
||||||
export const RountangleSVG = memo(function RountangleSVG(props: {rountangle: Rountangle; selected: string[]; highlight: RountanglePart[]; errors: string[]; active: boolean; }) {
|
export const RountangleSVG = memo(function RountangleSVG(props: {rountangle: Rountangle; selected: RountanglePart[]; highlight: RountanglePart[]; error?: string; active: boolean; }) {
|
||||||
|
console.log('render rountangle', props.rountangle.uid);
|
||||||
|
|
||||||
const { topLeft, size, uid } = props.rountangle;
|
const { topLeft, size, uid } = props.rountangle;
|
||||||
// always draw a rountangle with a minimum size
|
// always draw a rountangle with a minimum size
|
||||||
// during resizing, rountangle can be smaller than this size and even have a negative size, but we don't show it
|
// during resizing, rountangle can be smaller than this size and even have a negative size, but we don't show it
|
||||||
|
|
@ -14,7 +17,7 @@ export const RountangleSVG = memo(function RountangleSVG(props: {rountangle: Rou
|
||||||
className: 'rountangle'
|
className: 'rountangle'
|
||||||
+ (props.selected.length === 4 ? " selected" : "")
|
+ (props.selected.length === 4 ? " selected" : "")
|
||||||
+ (' ' + props.rountangle.kind)
|
+ (' ' + props.rountangle.kind)
|
||||||
+ (props.errors.length > 0 ? " error" : "")
|
+ (props.error ? " error" : "")
|
||||||
+ (props.active ? " active" : ""),
|
+ (props.active ? " active" : ""),
|
||||||
"data-uid": uid,
|
"data-uid": uid,
|
||||||
"data-parts": "left top right bottom",
|
"data-parts": "left top right bottom",
|
||||||
|
|
@ -31,11 +34,17 @@ export const RountangleSVG = memo(function RountangleSVG(props: {rountangle: Rou
|
||||||
|
|
||||||
<text x={10} y={20} className="uid">{props.rountangle.uid}</text>
|
<text x={10} y={20} className="uid">{props.rountangle.uid}</text>
|
||||||
|
|
||||||
{(props.errors.length > 0) &&
|
{props.error &&
|
||||||
<text className="error" x={10} y={40} data-uid={uid} data-parts="left top right bottom">{props.errors.join(' ')}</text>}
|
<text className="error" x={10} y={40} data-uid={uid} data-parts="left top right bottom">{props.error}</text>}
|
||||||
|
|
||||||
<RectHelper uid={uid} size={minSize}
|
<RectHelper uid={uid} size={minSize}
|
||||||
selected={props.selected}
|
selected={props.selected}
|
||||||
highlight={props.highlight} />
|
highlight={props.highlight} />
|
||||||
</g>;
|
</g>;
|
||||||
|
}, (prevProps, nextProps) => {
|
||||||
|
return prevProps.rountangle === nextProps.rountangle
|
||||||
|
&& arraysEqual(prevProps.selected, nextProps.selected)
|
||||||
|
&& arraysEqual(prevProps.highlight, nextProps.highlight)
|
||||||
|
&& prevProps.error === nextProps.error
|
||||||
|
&& prevProps.active === nextProps.active
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -718,9 +718,9 @@ export const VisualEditor = memo(function VisualEditor({state, setState, setAST,
|
||||||
rountangle={rountangle}
|
rountangle={rountangle}
|
||||||
selected={selection.find(r => r.uid === rountangle.uid)?.parts || []}
|
selected={selection.find(r => r.uid === rountangle.uid)?.parts || []}
|
||||||
highlight={[...(sidesToHighlight[rountangle.uid] || []), ...(rountanglesToHighlight[rountangle.uid]?["left","right","top","bottom"]:[]) as RountanglePart[]]}
|
highlight={[...(sidesToHighlight[rountangle.uid] || []), ...(rountanglesToHighlight[rountangle.uid]?["left","right","top","bottom"]:[]) as RountanglePart[]]}
|
||||||
errors={errors
|
error={errors
|
||||||
.filter(({shapeUid}) => shapeUid === rountangle.uid)
|
.filter(({shapeUid}) => shapeUid === rountangle.uid)
|
||||||
.map(({message}) => message)}
|
.map(({message}) => message).join(', ')}
|
||||||
active={highlightActive.has(rountangle.uid)}
|
active={highlightActive.has(rountangle.uid)}
|
||||||
/>})}
|
/>})}
|
||||||
|
|
||||||
|
|
@ -730,9 +730,9 @@ export const VisualEditor = memo(function VisualEditor({state, setState, setAST,
|
||||||
diamond={diamond}
|
diamond={diamond}
|
||||||
selected={selection.find(r => r.uid === diamond.uid)?.parts || []}
|
selected={selection.find(r => r.uid === diamond.uid)?.parts || []}
|
||||||
highlight={[...(sidesToHighlight[diamond.uid] || []), ...(rountanglesToHighlight[diamond.uid]?["left","right","top","bottom"]:[]) as RountanglePart[]]}
|
highlight={[...(sidesToHighlight[diamond.uid] || []), ...(rountanglesToHighlight[diamond.uid]?["left","right","top","bottom"]:[]) as RountanglePart[]]}
|
||||||
errors={errors
|
error={errors
|
||||||
.filter(({shapeUid}) => shapeUid === diamond.uid)
|
.filter(({shapeUid}) => shapeUid === diamond.uid)
|
||||||
.map(({message}) => message)}
|
.map(({message}) => message).join(', ')}
|
||||||
active={false}/>
|
active={false}/>
|
||||||
</>)}
|
</>)}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue