Skip to content

Commit f951ff1

Browse files
committed
Don't re-mount Group if defaultLayout changes
1 parent c4712cb commit f951ff1

File tree

2 files changed

+66
-6
lines changed

2 files changed

+66
-6
lines changed

lib/components/group/Group.test.tsx

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,47 @@ import { Panel } from "../panel/Panel";
66
import { Separator } from "../separator/Separator";
77
import { Group } from "./Group";
88
import { assert } from "../../utils/assert";
9+
import { eventEmitter } from "../../global/mutableState";
910

1011
describe("Group", () => {
12+
test("changes to defaultProps or disableCursor should not cause Group to remount", () => {
13+
const onMountedGroupsChange = vi.fn();
14+
const removeListener = eventEmitter.addListener(
15+
"mountedGroupsChange",
16+
onMountedGroupsChange
17+
);
18+
19+
const { rerender } = render(
20+
<Group
21+
defaultLayout={{
22+
a: 50,
23+
b: 50
24+
}}
25+
disableCursor={false}
26+
>
27+
<Panel id="a" />
28+
<Panel id="b" />
29+
</Group>
30+
);
31+
expect(onMountedGroupsChange).toHaveBeenCalledTimes(1);
32+
33+
rerender(
34+
<Group
35+
defaultLayout={{
36+
a: 35,
37+
b: 65
38+
}}
39+
disableCursor={true}
40+
>
41+
<Panel id="a" />
42+
<Panel id="b" />
43+
</Group>
44+
);
45+
expect(onMountedGroupsChange).toHaveBeenCalledTimes(1);
46+
47+
removeListener();
48+
});
49+
1150
describe("onLayoutChange", () => {
1251
beforeEach(() => {
1352
setElementBoundsFunction((element) => {

lib/components/group/Group.tsx

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"use client";
2-
import { useMemo, useRef, useState } from "react";
2+
import { useEffect, useMemo, useRef, useState } from "react";
33
import { mountGroup } from "../../global/mountGroup";
44
import { eventEmitter, read } from "../../global/mutableState";
55
import { layoutsEqual } from "../../global/utils/layoutsEqual";
@@ -15,6 +15,7 @@ import { GroupContext } from "./GroupContext";
1515
import { sortByElementOffset } from "./sortByElementOffset";
1616
import type { GroupProps, Layout, RegisteredGroup } from "./types";
1717
import { useGroupImperativeHandle } from "./useGroupImperativeHandle";
18+
import { useStableObject } from "../../hooks/useStableObject";
1819

1920
/**
2021
* A Group wraps a set of resizable Panel components.
@@ -97,6 +98,13 @@ export function Group({
9798
[id, orientation]
9899
);
99100

101+
const stableProps = useStableObject({
102+
defaultLayout,
103+
disableCursor
104+
});
105+
106+
const registeredGroupRef = useRef<RegisteredGroup | null>(null);
107+
100108
// Register Group and child Panels/Separators with global state
101109
// Listen to global state for drag state related to this Group
102110
useIsomorphicLayoutEffect(() => {
@@ -105,8 +113,8 @@ export function Group({
105113
}
106114

107115
const group: RegisteredGroup = {
108-
defaultLayout,
109-
disableCursor: !!disableCursor,
116+
defaultLayout: stableProps.defaultLayout,
117+
disableCursor: !!stableProps.disableCursor,
110118
disabled: !!disabled,
111119
element,
112120
id,
@@ -117,6 +125,8 @@ export function Group({
117125
separators
118126
};
119127

128+
registeredGroupRef.current = group;
129+
120130
const unmountGroup = mountGroup(group);
121131

122132
const globalState = read();
@@ -158,22 +168,33 @@ export function Group({
158168
);
159169

160170
return () => {
171+
registeredGroupRef.current = null;
172+
161173
unmountGroup();
162174
removeInteractionStateChangeListener();
163175
removeMountedGroupsChangeEventListener();
164176
};
165177
}, [
166-
defaultLayout,
167-
disableCursor,
168178
disabled,
169179
element,
170180
id,
171181
onLayoutChangeStable,
172182
orientation,
173183
panels,
174-
separators
184+
separators,
185+
stableProps
175186
]);
176187

188+
// Not all props require re-registering the group;
189+
// Some can be updated after the group has been registered
190+
useEffect(() => {
191+
const registeredGroup = registeredGroupRef.current;
192+
if (registeredGroup) {
193+
registeredGroup.defaultLayout = defaultLayout;
194+
registeredGroup.disableCursor = !!disableCursor;
195+
}
196+
});
197+
177198
// Panel layouts and Group dragging state are shared via CSS variables
178199
const cssVariables: { [key: string]: number | string | undefined } = {
179200
[POINTER_EVENTS_CSS_PROPERTY_NAME]: dragActive ? "none" : undefined

0 commit comments

Comments
 (0)