Fix shape drag perf (#1932)

This prevents geometry from being recalculated when dragging shapes
around. It uses an equality check on the shape props to opt out of
recalculations. This still allows bounds to be calculated based on other
reactive values, so if folks really want to use x,y values or opacity or
whatever, they can call editor.getShape(id) when making their
calculation.

### Change Type

- [x] `patch` — Bug fix
- [ ] `minor` — New feature
- [ ] `major` — Breaking change
- [ ] `dependencies` — Changes to package dependencies[^1]
- [ ] `documentation` — Changes to the documentation only[^2]
- [ ] `tests` — Changes to any test code only[^2]
- [ ] `internal` — Any other changes that don't affect the published
package[^2]
- [ ] I don't know

[^1]: publishes a `patch` release, for devDependencies use `internal`
[^2]: will not publish a new version


### Release Notes

- Fixes a perf regression for dragging shapes around
This commit is contained in:
David Sheldrick 2023-09-19 16:29:13 +01:00 committed by GitHub
parent db58d9c54f
commit 386a2396d1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 16 additions and 8 deletions

View file

@ -3724,9 +3724,11 @@ export class Editor extends EventEmitter<TLEventMap> {
@computed @computed
private get _shapeGeometryCache(): ComputedCache<Geometry2d, TLShape> { private get _shapeGeometryCache(): ComputedCache<Geometry2d, TLShape> {
return this.store.createComputedCache('bounds', (shape) => { return this.store.createComputedCache(
return this.getShapeUtil(shape).getGeometry(shape) 'bounds',
}) (shape) => this.getShapeUtil(shape).getGeometry(shape),
(a, b) => a.props === b.props
)
} }
/** /**

View file

@ -230,7 +230,7 @@ export class Store<R extends UnknownRecord = UnknownRecord, Props = unknown> {
// (undocumented) // (undocumented)
applyDiff(diff: RecordsDiff<R>, runCallbacks?: boolean): void; applyDiff(diff: RecordsDiff<R>, runCallbacks?: boolean): void;
clear: () => void; clear: () => void;
createComputedCache: <T, V extends R = R>(name: string, derive: (record: V) => T | undefined) => ComputedCache<T, V>; createComputedCache: <T, V extends R = R>(name: string, derive: (record: V) => T | undefined, isEqual?: ((a: V, b: V) => boolean) | undefined) => ComputedCache<T, V>;
createSelectedComputedCache: <T, J, V extends R = R>(name: string, selector: (record: V) => T | undefined, derive: (input: T) => J | undefined) => ComputedCache<J, V>; createSelectedComputedCache: <T, J, V extends R = R>(name: string, selector: (record: V) => T | undefined, derive: (input: T) => J | undefined) => ComputedCache<J, V>;
// @internal (undocumented) // @internal (undocumented)
ensureStoreIsUsable(): void; ensureStoreIsUsable(): void;

View file

@ -766,7 +766,8 @@ export class Store<R extends UnknownRecord = UnknownRecord, Props = unknown> {
*/ */
createComputedCache = <T, V extends R = R>( createComputedCache = <T, V extends R = R>(
name: string, name: string,
derive: (record: V) => T | undefined derive: (record: V) => T | undefined,
isEqual?: (a: V, b: V) => boolean
): ComputedCache<T, V> => { ): ComputedCache<T, V> => {
const cache = new Cache<Atom<any>, Computed<T | undefined>>() const cache = new Cache<Atom<any>, Computed<T | undefined>>()
return { return {
@ -775,9 +776,14 @@ export class Store<R extends UnknownRecord = UnknownRecord, Props = unknown> {
if (!atom) { if (!atom) {
return undefined return undefined
} }
return cache.get(atom, () => return cache.get(atom, () => {
computed<T | undefined>(name + ':' + id, () => derive(atom.value as V)) const recordSignal = isEqual
).value ? computed(atom.name + ':equals', () => atom.value, { isEqual })
: atom
return computed<T | undefined>(name + ':' + id, () => {
return derive(recordSignal.value as V)
})
}).value
}, },
} }
} }