Skip to content

Commit 7bedc71

Browse files
committed
[Page Actions] Fix render issues
1 parent ea2a45b commit 7bedc71

File tree

3 files changed

+78
-15
lines changed

3 files changed

+78
-15
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@shopify/polaris': patch
3+
---
4+
5+
Fixed a re-render bug with Page Actions

polaris-react/playground/DetailsPage.tsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -463,10 +463,10 @@ export function DetailsPage() {
463463
</VisuallyHidden>
464464
);
465465

466-
// ---- Description ----
466+
const [title, setTitle] = useState(
467+
"The North Face Ventrix Active Trail Hybrid Hoodie - Men's",
468+
);
467469
const [descriptionValue, setDescriptionValue] = useState(initialDescription);
468-
469-
// ---- Select ----
470470
const [selected, setSelected] = useState('today');
471471

472472
const options = [
@@ -546,7 +546,7 @@ export function DetailsPage() {
546546
<Page
547547
fullWidth
548548
breadcrumbs={[{content: 'Products', url: '/products/31'}]}
549-
title="The North Face Ventrix Active Trail Hybrid Hoodie - Men's"
549+
title={title}
550550
titleMetadata={<Badge status="success">Success badge</Badge>}
551551
primaryAction={{
552552
content: 'Save this page',
@@ -597,8 +597,11 @@ export function DetailsPage() {
597597
<FormLayout>
598598
<TextField
599599
label="Title"
600-
value="M60-A"
601-
onChange={() => setIsDirty(true)}
600+
value={title}
601+
onChange={(title) => {
602+
setTitle(title);
603+
setIsDirty(true);
604+
}}
602605
autoComplete="off"
603606
/>
604607
<TextField

polaris-react/src/components/ActionMenu/components/Actions/Actions.tsx

Lines changed: 64 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
1+
/* eslint-disable no-console */
2+
import React, {
3+
useCallback,
4+
useEffect,
5+
useLayoutEffect,
6+
useMemo,
7+
useRef,
8+
useState,
9+
} from 'react';
210

311
import {debounce} from '../../../../utilities/debounce';
412
import {useI18n} from '../../../../utilities/i18n';
@@ -9,10 +17,10 @@ import type {
917
MenuGroupDescriptor,
1018
} from '../../../../types';
1119
import {ButtonGroup} from '../../../ButtonGroup';
12-
// eslint-disable-next-line import/no-deprecated
13-
import {EventListener} from '../../../EventListener';
1420
import {MenuGroup} from '../MenuGroup';
1521
import {SecondaryAction} from '../SecondaryAction';
22+
import {useEventListener} from '../../../../utilities/use-event-listener';
23+
import {useIsAfterInitialMount} from '../../../../utilities/use-is-after-initial-mount';
1624

1725
import styles from './Actions.scss';
1826

@@ -33,6 +41,12 @@ interface MeasuredActions {
3341
const ACTION_SPACING = 8;
3442

3543
export function Actions({actions = [], groups = [], onActionRollup}: Props) {
44+
const isMounted = useIsAfterInitialMount();
45+
useEffect(() => {
46+
isMounted
47+
? console.log('update, measured: ', timesMeasured.current)
48+
: console.log('mount, measured: ', timesMeasured.current);
49+
});
3650
const i18n = useI18n();
3751
const actionsLayoutRef = useRef<HTMLDivElement>(null);
3852
const menuGroupWidthRef = useRef<number>(0);
@@ -81,6 +95,10 @@ export function Actions({actions = [], groups = [], onActionRollup}: Props) {
8195
);
8296
}
8397

98+
console.log(
99+
'updateActions => setMeasuredActions, measured: ',
100+
timesMeasured.current,
101+
);
84102
setMeasuredActions((currentMeasuredActions) => {
85103
const showable = actionsAndGroups.slice(
86104
0,
@@ -90,6 +108,16 @@ export function Actions({actions = [], groups = [], onActionRollup}: Props) {
90108
currentMeasuredActions.showable.length,
91109
actionsAndGroups.length,
92110
);
111+
112+
if (
113+
showable === currentMeasuredActions.showable &&
114+
rolledUp === currentMeasuredActions.rolledUp
115+
) {
116+
// Bail out of setState if the values are the same
117+
console.log('updateActions => bail out of setState');
118+
return currentMeasuredActions;
119+
}
120+
93121
return {showable, rolledUp};
94122
});
95123
}, [actions, groups]);
@@ -105,6 +133,10 @@ export function Actions({actions = [], groups = [], onActionRollup}: Props) {
105133
const actionsAndGroups = [...actions, ...groups];
106134

107135
if (actionsAndGroups.length === 1) {
136+
console.log(
137+
'measureActions => setMeasuredActions, measured: ',
138+
timesMeasured.current,
139+
);
108140
setMeasuredActions({showable: actionsAndGroups, rolledUp: []});
109141
return;
110142
}
@@ -143,10 +175,23 @@ export function Actions({actions = [], groups = [], onActionRollup}: Props) {
143175
rollupActiveRef.current = isRollupActive;
144176
}
145177
}
178+
console.log(
179+
'measureActions => setMeasuredActions, measured: ',
180+
timesMeasured.current,
181+
);
182+
setMeasuredActions((currentMeasuredActions) => {
183+
if (
184+
currentMeasuredActions.showable === newShowableActions &&
185+
currentMeasuredActions.rolledUp === newRolledUpActions
186+
) {
187+
console.log('measureActions bail out of setState');
188+
return currentMeasuredActions;
189+
}
146190

147-
setMeasuredActions({
148-
showable: newShowableActions,
149-
rolledUp: newRolledUpActions,
191+
return {
192+
showable: newShowableActions,
193+
rolledUp: newRolledUpActions,
194+
};
150195
});
151196

152197
timesMeasured.current += 1;
@@ -161,6 +206,10 @@ export function Actions({actions = [], groups = [], onActionRollup}: Props) {
161206
availableWidthRef.current = actionsLayoutRef.current.offsetWidth;
162207
// Set timesMeasured to 0 to allow re-measuring
163208
timesMeasured.current = 0;
209+
console.log(
210+
'handleResize => measureActions, measured: ',
211+
timesMeasured.current,
212+
);
164213
measureActions();
165214
},
166215
50,
@@ -169,7 +218,9 @@ export function Actions({actions = [], groups = [], onActionRollup}: Props) {
169218
[measureActions],
170219
);
171220

172-
useEffect(() => {
221+
useEventListener('resize', handleResize);
222+
223+
useLayoutEffect(() => {
173224
if (!actionsLayoutRef.current) {
174225
return;
175226
}
@@ -178,12 +229,17 @@ export function Actions({actions = [], groups = [], onActionRollup}: Props) {
178229
if (
179230
// Allow measuring twice
180231
// This accounts for the initial paint and re-flow
181-
timesMeasured.current >= 2 &&
232+
timesMeasured.current >= 3 &&
182233
[...actions, ...groups].length === actionsAndGroupsLengthRef.current
183234
) {
235+
console.log(
236+
'useEffect, updateActions, measured: ',
237+
timesMeasured.current,
238+
);
184239
updateActions();
185240
return;
186241
}
242+
console.log('useEffect, measureActions, measured: ', timesMeasured.current);
187243
measureActions();
188244
}, [actions, groups, measureActions, updateActions]);
189245

@@ -319,7 +375,6 @@ export function Actions({actions = [], groups = [], onActionRollup}: Props) {
319375
return (
320376
<div className={styles.ActionsLayout} ref={actionsLayoutRef}>
321377
{groupedActionsMarkup}
322-
<EventListener event="resize" handler={handleResize} />
323378
</div>
324379
);
325380
}

0 commit comments

Comments
 (0)