Skip to content

Commit c216065

Browse files
authored
Fix React StrictMode useCollection (#4013)
* Fix React StrictMode useCollection
1 parent ee3e90b commit c216065

File tree

5 files changed

+24
-28
lines changed

5 files changed

+24
-28
lines changed

packages/@react-spectrum/tabs/src/Tabs.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ export function TabPanels<T>(props: SpectrumTabPanelsProps<T>) {
337337
const {tabState, tabProps} = useContext(TabContext);
338338
const {tabListState} = tabState;
339339

340-
const factory = nodes => new ListCollection(nodes);
340+
const factory = useCallback(nodes => new ListCollection(nodes), []);
341341
const collection = useCollection({items: tabProps.items, ...props}, factory, {suppressTextValueWarning: true});
342342
const selectedItem = tabListState ? collection.getItem(tabListState.selectedKey) : null;
343343

packages/@react-stately/collections/src/useCollection.ts

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,21 @@
1010
* governing permissions and limitations under the License.
1111
*/
1212

13-
import {Collection, CollectionBase, CollectionStateBase, Node} from '@react-types/shared';
13+
import {Collection, CollectionStateBase, Node} from '@react-types/shared';
1414
import {CollectionBuilder} from './CollectionBuilder';
15-
import {useMemo, useRef} from 'react';
15+
import {useMemo} from 'react';
1616

17-
type CollectionFactory<T, C extends Collection<Node<T>>> = (node: Iterable<Node<T>>, prev: C | null) => C;
17+
type CollectionFactory<T, C extends Collection<Node<T>>> = (node: Iterable<Node<T>>) => C;
1818

19-
export function useCollection<T extends object, C extends Collection<Node<T>> = Collection<Node<T>>>(props: CollectionStateBase<T, C>, factory: CollectionFactory<T, C>, context?: unknown, invalidators: Array<any> = []): C {
19+
export function useCollection<T extends object, C extends Collection<Node<T>> = Collection<Node<T>>>(props: CollectionStateBase<T, C>, factory: CollectionFactory<T, C>, context?: unknown): C {
2020
let builder = useMemo(() => new CollectionBuilder<T>(), []);
21-
22-
let prev = useRef<C>(null);
23-
return useMemo(() => {
24-
if (props.collection) {
25-
return props.collection;
21+
let {children, items, collection} = props;
22+
let result = useMemo(() => {
23+
if (collection) {
24+
return collection;
2625
}
27-
28-
let nodes = builder.build(props as CollectionBase<T>, context);
29-
prev.current = factory(nodes, prev.current);
30-
return prev.current;
31-
// Don't invalidate when any prop changes, just the two we care about.
32-
// eslint-disable-next-line react-hooks/exhaustive-deps
33-
}, [builder, props.children, props.items, props.collection, context, ...invalidators]);
26+
let nodes = builder.build({children, items}, context);
27+
return factory(nodes);
28+
}, [builder, children, items, collection, context, factory]);
29+
return result;
3430
}

packages/@react-stately/list/src/useListState.ts

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

1313
import {Collection, CollectionStateBase, Node} from '@react-types/shared';
14-
import {Key, useEffect, useMemo, useRef} from 'react';
14+
import {Key, useCallback, useEffect, useMemo, useRef} from 'react';
1515
import {ListCollection} from './ListCollection';
1616
import {MultipleSelectionStateProps, SelectionManager, useMultipleSelectionState} from '@react-stately/selection';
1717
import {useCollection} from '@react-stately/collections';
@@ -46,10 +46,10 @@ export function useListState<T extends object>(props: ListProps<T>): ListState<T
4646
props.disabledKeys ? new Set(props.disabledKeys) : new Set<Key>()
4747
, [props.disabledKeys]);
4848

49-
let factory = nodes => filter ? new ListCollection(filter(nodes)) : new ListCollection(nodes as Iterable<Node<T>>);
49+
let factory = useCallback(nodes => filter ? new ListCollection(filter(nodes)) : new ListCollection(nodes as Iterable<Node<T>>), [filter]);
5050
let context = useMemo(() => ({suppressTextValueWarning: props.suppressTextValueWarning}), [props.suppressTextValueWarning]);
5151

52-
let collection = useCollection(props, factory, context, [filter]);
52+
let collection = useCollection(props, factory, context);
5353

5454
let selectionManager = useMemo(() =>
5555
new SelectionManager(collection, selectionState)

packages/@react-stately/table/src/useTableState.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import {CollectionStateBase, Node, SelectionMode, Sortable, SortDescriptor, SortDirection} from '@react-types/shared';
1414
import {GridState, useGridState} from '@react-stately/grid';
1515
import {TableCollection as ITableCollection} from '@react-types/table';
16-
import {Key, useMemo, useState} from 'react';
16+
import {Key, useCallback, useMemo, useState} from 'react';
1717
import {MultipleSelectionStateProps} from '@react-stately/selection';
1818
import {TableCollection} from './TableCollection';
1919
import {useCollection} from '@react-stately/collections';
@@ -60,19 +60,19 @@ const OPPOSITE_SORT_DIRECTION = {
6060
*/
6161
export function useTableState<T extends object>(props: TableStateProps<T>): TableState<T> {
6262
let [isKeyboardNavigationDisabled, setKeyboardNavigationDisabled] = useState(false);
63-
let {selectionMode = 'none'} = props;
63+
let {selectionMode = 'none', showSelectionCheckboxes, showDragButtons} = props;
6464

6565
let context = useMemo(() => ({
66-
showSelectionCheckboxes: props.showSelectionCheckboxes && selectionMode !== 'none',
67-
showDragButtons: props.showDragButtons,
66+
showSelectionCheckboxes: showSelectionCheckboxes && selectionMode !== 'none',
67+
showDragButtons: showDragButtons,
6868
selectionMode,
6969
columns: []
7070
// eslint-disable-next-line react-hooks/exhaustive-deps
71-
}), [props.children, props.showSelectionCheckboxes, selectionMode, props.showDragButtons]);
71+
}), [props.children, showSelectionCheckboxes, selectionMode, showDragButtons]);
7272

7373
let collection = useCollection<T, ITableCollection<T>>(
7474
props,
75-
(nodes, prev) => new TableCollection(nodes, prev, context),
75+
useCallback((nodes) => new TableCollection(nodes, null, context), [context]),
7676
context
7777
);
7878
let {disabledKeys, selectionManager} = useGridState({

packages/@react-stately/tree/src/useTreeState.ts

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

1313
import {Collection, CollectionStateBase, Expandable, MultipleSelection, Node} from '@react-types/shared';
14-
import {Key, useEffect, useMemo} from 'react';
14+
import {Key, useCallback, useEffect, useMemo} from 'react';
1515
import {SelectionManager, useMultipleSelectionState} from '@react-stately/selection';
1616
import {TreeCollection} from './TreeCollection';
1717
import {useCollection} from '@react-stately/collections';
@@ -51,7 +51,7 @@ export function useTreeState<T extends object>(props: TreeProps<T>): TreeState<T
5151
props.disabledKeys ? new Set(props.disabledKeys) : new Set<Key>()
5252
, [props.disabledKeys]);
5353

54-
let tree = useCollection(props, nodes => new TreeCollection(nodes, {expandedKeys}), null, [expandedKeys]);
54+
let tree = useCollection(props, useCallback(nodes => new TreeCollection(nodes, {expandedKeys}), [expandedKeys]), null);
5555

5656
// Reset focused key if that item is deleted from the collection.
5757
useEffect(() => {

0 commit comments

Comments
 (0)