Skip to content

Commit ab5e6f3

Browse files
authored
chore: Update eslint plugin react hooks followup (#9054)
* chore: update eslint plugin react hooks * fix all lint errors * Add lines for other config items * Revert "fix all lint errors" This reverts commit 7b96414. * fix all rules of hooks and exhaustive dependencies * enable all rules we are already passing * fix event order and cleanup * fix lint * turn on and fix static-components * turn on and fix set-state-in-effect * turn on and fix purity * fix lower react version tests * turn on globals and fix errors * move event listener attachment to an effect * convert remaining useElementTypes * fix read from ref in render
1 parent 55239ac commit ab5e6f3

File tree

23 files changed

+262
-155
lines changed

23 files changed

+262
-155
lines changed

eslint.config.mjs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -234,14 +234,14 @@ export default [{
234234
'react-hooks/error-boundaries': ERROR,
235235
'react-hooks/component-hook-factories': ERROR,
236236
'react-hooks/gating': ERROR,
237-
// 'react-hooks/globals': ERROR,
237+
'react-hooks/globals': ERROR,
238238
// 'react-hooks/immutability': ERROR,
239-
// 'react-hooks/preserve-manual-memoization': ERROR,
240-
// 'react-hooks/purity': ERROR,
241-
// 'react-hooks/refs': ERROR,
242-
// 'react-hooks/set-state-in-effect': ERROR,
239+
// 'react-hooks/preserve-manual-memoization': ERROR, // No idea how to turn this one on yet
240+
'react-hooks/purity': ERROR,
241+
// 'react-hooks/refs': ERROR, // can't turn on until https://github.com/facebook/react/issues/34775 is fixed
242+
'react-hooks/set-state-in-effect': ERROR,
243243
'react-hooks/set-state-in-render': ERROR,
244-
// 'react-hooks/static-components': ERROR,
244+
'react-hooks/static-components': ERROR,
245245
'react-hooks/unsupported-syntax': WARN,
246246
'react-hooks/use-memo': ERROR,
247247
'react-hooks/incompatible-library': WARN,

packages/@react-aria/dnd/stories/Reorderable.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ export function ReorderableGridExample(props: any): JSX.Element {
6565
);
6666
}
6767

68+
let randomDragTypeReorderExample = `keys-${Math.random().toString(36).slice(2)}`;
6869
function ReorderableGrid(props) {
6970
let ref = React.useRef<HTMLDivElement>(null);
7071
let state = useListState(props);
@@ -91,7 +92,7 @@ function ReorderableGrid(props) {
9192
});
9293

9394
// Use a random drag type so the items can only be reordered within this list and not dragged elsewhere.
94-
let dragType = React.useMemo(() => `keys-${Math.random().toString(36).slice(2)}`, []);
95+
let dragType = React.useMemo(() => randomDragTypeReorderExample, []);
9596
let preview = useRef(null);
9697
let dragState = useDraggableCollectionState({
9798
collection: gridState.collection,

packages/@react-aria/dnd/test/dnd.test.js

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {CUSTOM_DRAG_TYPE} from '../src/constants';
1717
import {DataTransfer, DataTransferItem, DragEvent, FileSystemDirectoryEntry, FileSystemFileEntry} from './mocks';
1818
import {Draggable, Droppable} from './examples';
1919
import {DragTypes} from '../src/utils';
20-
import React from 'react';
20+
import React, {useEffect} from 'react';
2121
import userEvent from '@testing-library/user-event';
2222

2323
function pointerEvent(type, opts) {
@@ -195,13 +195,13 @@ describe('useDrag and useDrop', function () {
195195
let draggable = tree.getByText('Drag me');
196196
let droppable = tree.getByText('Drop here');
197197
expect(droppable).toHaveAttribute('data-droptarget', 'false');
198-
198+
199199
let dataTransfer = new DataTransfer();
200200
fireEvent(draggable, new DragEvent('dragstart', {dataTransfer, clientX: 0, clientY: 0}));
201201
act(() => jest.runAllTimers());
202202
expect(draggable).toHaveAttribute('data-dragging', 'true');
203203
expect(droppable).toHaveAttribute('data-droptarget', 'false');
204-
204+
205205
expect(onDragStart).toHaveBeenCalledTimes(1);
206206
expect(onDragMove).not.toHaveBeenCalled();
207207
expect(onDragEnd).not.toHaveBeenCalled();
@@ -2574,7 +2574,9 @@ describe('useDrag and useDrop', function () {
25742574
let setShowTarget2;
25752575
let Test = () => {
25762576
let [showTarget2, _setShowTarget2] = React.useState(false);
2577-
setShowTarget2 = _setShowTarget2;
2577+
useEffect(() => {
2578+
setShowTarget2 = _setShowTarget2;
2579+
}, [_setShowTarget2]);
25782580
return (<>
25792581
<Draggable />
25802582
<Droppable />
@@ -2635,7 +2637,9 @@ describe('useDrag and useDrop', function () {
26352637
let setShowTarget2;
26362638
let Test = () => {
26372639
let [showTarget2, _setShowTarget2] = React.useState(true);
2638-
setShowTarget2 = _setShowTarget2;
2640+
useEffect(() => {
2641+
setShowTarget2 = _setShowTarget2;
2642+
}, [_setShowTarget2]);
26392643
return (<>
26402644
<Draggable />
26412645
<Droppable />

packages/@react-aria/menu/src/useSafelyMouseToSubmenu.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
import {RefObject} from '@react-types/shared';
33
import {useEffect, useRef, useState} from 'react';
4-
import {useEffectEvent, useResizeObserver} from '@react-aria/utils';
4+
import {useEffectEvent, useLayoutEffect, useResizeObserver} from '@react-aria/utils';
55
import {useInteractionModality} from '@react-aria/interactions';
66

77
interface SafelyMouseToSubmenuOptions {
@@ -67,7 +67,7 @@ export function useSafelyMouseToSubmenu(options: SafelyMouseToSubmenuOptions): v
6767
}
6868
}, [menuRef, preventPointerEvents]);
6969

70-
useEffect(() => {
70+
useLayoutEffect(() => {
7171
let submenu = submenuRef.current;
7272
let menu = menuRef.current;
7373

packages/@react-aria/toast/src/useToast.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
import {AriaButtonProps} from '@react-types/button';
1414
import {AriaLabelingProps, DOMAttributes, FocusableElement, RefObject} from '@react-types/shared';
15-
import {filterDOMProps, useId, useSlotId} from '@react-aria/utils';
15+
import {filterDOMProps, useId, useLayoutEffect, useSlotId} from '@react-aria/utils';
1616
// @ts-ignore
1717
import intlMessages from '../intl/*.json';
1818
import {QueuedToast, ToastState} from '@react-stately/toast';
@@ -68,7 +68,7 @@ export function useToast<T>(props: AriaToastProps<T>, state: ToastState<T>, ref:
6868
// Originally was tied to animationStart/End via https://github.com/adobe/react-spectrum/pull/6223/commits/e22e319df64958e822ab7cd9685e96818cae9ba5
6969
// but toasts don't always have animations.
7070
let [isVisible, setIsVisible] = useState(false);
71-
useEffect(() => {
71+
useLayoutEffect(() => {
7272
setIsVisible(true);
7373
}, []);
7474

packages/@react-aria/utils/test/mergeRefs.test.tsx

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
*/
1212

1313
import {mergeRefs} from '../';
14-
import React, {useCallback, useRef} from 'react';
14+
import React, {useCallback, useEffect, useRef} from 'react';
1515
import {render} from '@react-spectrum/test-utils-internal';
1616

1717
describe('mergeRefs', () => {
@@ -20,16 +20,21 @@ describe('mergeRefs', () => {
2020
let ref2;
2121

2222
const TextField = (props) => {
23-
ref1 = useRef(null);
24-
ref2 = useRef(null);
23+
let internalRef1 = useRef(null);
24+
let internalRef2 = useRef(null);
25+
useEffect(() => {
26+
ref1 = internalRef1;
27+
ref2 = internalRef2;
28+
}, [internalRef1, internalRef2]);
2529

26-
const ref = mergeRefs(ref1, ref2);
30+
const ref = mergeRefs(internalRef1, internalRef2);
2731
return <input {...props} ref={ref} />;
2832
};
2933

3034
render(<TextField foo="foo" />);
3135

3236
expect(ref1.current).toBe(ref2.current);
37+
expect(ref1.current).not.toBeNull();
3338
});
3439

3540
if (parseInt(React.version.split('.')[0], 10) >= 19) {
@@ -40,14 +45,18 @@ describe('mergeRefs', () => {
4045
let target = null;
4146

4247
const TextField = (props) => {
43-
ref1 = useRef(null);
44-
ref2 = useRef(null);
48+
let internalRef1 = useRef(null);
49+
let internalRef2 = useRef(null);
50+
useEffect(() => {
51+
ref1 = internalRef1;
52+
ref2 = internalRef2;
53+
}, [internalRef1, internalRef2]);
4554
let ref3 = useCallback((node) => {
4655
target = node;
4756
return cleanUp;
4857
}, []);
4958

50-
const ref = mergeRefs(ref1, ref2, ref3);
59+
const ref = mergeRefs(internalRef1, internalRef2, ref3);
5160
return <input {...props} ref={ref} />;
5261
};
5362

packages/@react-spectrum/autocomplete/src/SearchAutocomplete.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,6 @@ function ForwardSearchAutocompleteInput<T>(props: SearchAutocompleteInputProps<T
288288
}
289289
} else if (!isLoading) {
290290
// If loading is no longer happening, clear any timers and hide the loading circle
291-
setShowLoading(false);
292291
if (timeout.current != null) {
293292
clearTimeout(timeout.current);
294293
timeout.current = null;
@@ -297,6 +296,11 @@ function ForwardSearchAutocompleteInput<T>(props: SearchAutocompleteInputProps<T
297296

298297
lastInputValue.current = inputValue;
299298
}, [isLoading, showLoading, inputValue]);
299+
let [prevIsLoading, setPrevIsLoading] = useState(isLoading);
300+
if (prevIsLoading !== isLoading && !isLoading) {
301+
setShowLoading(false);
302+
setPrevIsLoading(isLoading);
303+
}
300304

301305
return (
302306
(<FocusRing

packages/@react-spectrum/breadcrumbs/test/Breadcrumbs.test.js

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {act, pointerMap, render, within} from '@react-spectrum/test-utils-intern
1414
import {Breadcrumbs} from '../';
1515
import {Item} from '@react-stately/collections';
1616
import {Provider} from '@react-spectrum/provider';
17-
import React, {useRef} from 'react';
17+
import React, {createRef, forwardRef} from 'react';
1818
import {theme} from '@react-spectrum/theme-default';
1919
import userEvent from '@testing-library/user-event';
2020

@@ -93,16 +93,15 @@ describe('Breadcrumbs', function () {
9393
});
9494

9595
it('Should handle forward ref', function () {
96-
let ref;
97-
let Component = () => {
98-
ref = useRef();
96+
let ref = createRef();
97+
let Component = forwardRef((props, forwardedRef) => {
9998
return (
100-
<Breadcrumbs ref={ref} aria-label="breadcrumbs-test">
99+
<Breadcrumbs ref={forwardedRef} aria-label="breadcrumbs-test">
101100
<Item>Folder 1</Item>
102101
</Breadcrumbs>
103102
);
104-
};
105-
let {getByLabelText} = render(<Component />);
103+
});
104+
let {getByLabelText} = render(<Component ref={ref} />);
106105
let breadcrumb = getByLabelText('breadcrumbs-test');
107106
expect(breadcrumb).toBe(ref.current.UNSAFE_getDOMNode());
108107
});

packages/@react-spectrum/color/src/ColorWheel.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ import {classNames, dimensionValue, useFocusableRef, useStyleProps} from '@react
1414
import {ColorThumb} from './ColorThumb';
1515
import {ColorWheelContext, useContextProps} from 'react-aria-components';
1616
import {FocusableRef} from '@react-types/shared';
17-
import React, {useCallback, useEffect, useRef, useState} from 'react';
17+
import React, {useCallback, useRef, useState} from 'react';
1818
import {SpectrumColorWheelProps} from '@react-types/color';
1919
import styles from '@adobe/spectrum-css-temp/components/colorwheel/vars.css';
2020
import {useColorWheel} from '@react-aria/color';
2121
import {useColorWheelState} from '@react-stately/color';
2222
import {useFocusRing} from '@react-aria/focus';
23+
import {useLayoutEffect, useResizeObserver} from '@react-aria/utils';
2324
import {useProviderProps} from '@react-spectrum/provider';
24-
import {useResizeObserver} from '@react-aria/utils';
2525

2626
const WHEEL_THICKNESS = 24;
2727

@@ -53,7 +53,7 @@ export const ColorWheel = React.forwardRef(function ColorWheel(props: SpectrumCo
5353
}
5454
}, [containerRef, setWheelRadius, setWheelThickness]);
5555

56-
useEffect(() => {
56+
useLayoutEffect(() => {
5757
// the size observer's fallback to the window resize event doesn't fire on mount
5858
if (wheelRadius === 0) {
5959
resizeHandler();

packages/@react-spectrum/color/stories/ColorSwatchPicker.stories.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,12 @@ export const Default: ColorSwatchPickerStory = (args) => (
4848
</ColorSwatchPicker>
4949
);
5050

51+
let randomColors = Array.from(Array(24)).map(() => {
52+
return `#${Math.floor(Math.random() * 0xffffff).toString(16).padStart(6, '0')}`;
53+
});
5154
export const ManySwatches: ColorSwatchPickerStory = (args) => (
5255
<ColorSwatchPicker {...args} maxWidth="size-3000">
53-
{Array.from(Array(24)).map(() => {
54-
let color = `#${Math.floor(Math.random() * 0xffffff).toString(16).padStart(6, '0')}`;
56+
{randomColors.map((color) => {
5557
return <ColorSwatch key={color} color={color} />;
5658
})}
5759
</ColorSwatchPicker>

0 commit comments

Comments
 (0)