downshift 9.4.0-alpha.2 → 9.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/downshift.cjs.cjs +132 -126
  2. package/dist/downshift.d.ts +60 -56
  3. package/dist/downshift.esm.mjs +132 -126
  4. package/dist/downshift.native.cjs.cjs +129 -126
  5. package/dist/downshift.nativeweb.cjs.cjs +132 -126
  6. package/dist/downshift.umd.js +132 -126
  7. package/dist/downshift.umd.js.map +1 -1
  8. package/dist/downshift.umd.min.js +1 -1
  9. package/dist/downshift.umd.min.js.map +1 -1
  10. package/dist/hooks/testUtils/interactions.d.ts +7 -7
  11. package/dist/hooks/useCombobox/__tests__/utils/renderCombobox.d.ts +3 -59
  12. package/dist/hooks/useCombobox/__tests__/utils/renderUseCombobox.d.ts +1 -1
  13. package/dist/hooks/useCombobox/index.types.d.ts +0 -1
  14. package/dist/hooks/useCombobox/utils/getInitialState.d.ts +1 -6
  15. package/dist/hooks/useCombobox/utils/propTypes.d.ts +1 -43
  16. package/dist/hooks/useSelect/__tests__/utils/getItemIndexByCharacter.d.ts +1 -1
  17. package/dist/hooks/useSelect/__tests__/utils/renderSelect.d.ts +2 -58
  18. package/dist/hooks/useSelect/__tests__/utils/renderUseSelect.d.ts +1 -1
  19. package/dist/hooks/useSelect/__tests__/utils/stateChangeTestCases.d.ts +29 -50
  20. package/dist/hooks/useSelect/utils/propTypes.d.ts +1 -38
  21. package/dist/hooks/useTagGroup/__tests__/utils/renderTagGroup.d.ts +1 -63
  22. package/dist/hooks/useTagGroup/__tests__/utils/renderUseTagGroup.d.ts +2 -2
  23. package/dist/hooks/utils/dropdownDefaultProps.d.ts +3 -4
  24. package/dist/hooks/utils/useMouseAndTouchTracker.d.ts +3 -3
  25. package/dist/hooks/utils/useScrollIntoView.d.ts +4 -3
  26. package/dist/utils/getHighlightedIndex.d.ts +1 -1
  27. package/dist/utils/getNonDisabledIndex.d.ts +1 -1
  28. package/dist/utils/index.d.ts +1 -0
  29. package/dist/utils/useIsomorphicLayoutEffect.d.ts +2 -0
  30. package/dist/utils/useLatestRef.d.ts +1 -1
  31. package/package.json +15 -2
  32. package/preact/dist/downshift.cjs.cjs +132 -126
  33. package/preact/dist/downshift.esm.mjs +132 -126
  34. package/preact/dist/downshift.umd.js +132 -126
  35. package/preact/dist/downshift.umd.js.map +1 -1
  36. package/preact/dist/downshift.umd.min.js +1 -1
  37. package/preact/dist/downshift.umd.min.js.map +1 -1
  38. package/dist/hooks/useMultipleSelection/index.d.ts +0 -50
  39. package/dist/hooks/useMultipleSelection/reducer.d.ts +0 -1
  40. package/dist/hooks/useMultipleSelection/stateChangeTypes.d.ts +0 -13
  41. package/dist/hooks/useMultipleSelection/utils.d.ts +0 -44
  42. package/dist/types.d.ts +0 -12
  43. package/preact/dist/downshift.cjs.js +0 -4265
  44. package/preact/dist/downshift.esm.js +0 -4238
@@ -68,14 +68,18 @@ function resetIdCounter() {
68
68
  idCounter = 0;
69
69
  }
70
70
 
71
- function useLatestRef(val) {
72
- var ref = React.useRef(val);
73
- // technically this is not "concurrent mode safe" because we're manipulating
74
- // the value during render (so it's not idempotent). However, the places this
75
- // hook is used is to support memoizing callbacks which will be called
76
- // *during* render, so we need the latest values *during* render.
77
- // If not for this, then we'd probably want to use useLayoutEffect instead.
78
- ref.current = val;
71
+ // istanbul ignore next
72
+ var canUseDOM = typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined';
73
+
74
+ // React Native supports layout effects even though it does not have a DOM.
75
+ // istanbul ignore next
76
+ var useIsomorphicLayoutEffect = canUseDOM || false ? React.useLayoutEffect : React.useEffect;
77
+
78
+ function useLatestRef(value) {
79
+ var ref = React.useRef(value);
80
+ useIsomorphicLayoutEffect(function () {
81
+ ref.current = value;
82
+ }, [value]);
79
83
  return ref;
80
84
  }
81
85
 
@@ -1602,18 +1606,27 @@ function invokeOnChangeHandler(key, action, props, state, newState) {
1602
1606
  */
1603
1607
  function useEnhancedReducer(reducer, props, createInitialState, isStateEqual) {
1604
1608
  var prevStateRef = React.useRef({});
1605
- var actionRef = React.useRef();
1606
- var enhancedReducer = React.useCallback(function (state, action) {
1607
- actionRef.current = action;
1609
+ var enhancedReducer = React.useCallback(function (_ref, action) {
1610
+ var state = _ref.state;
1608
1611
  state = getState(state, action.props);
1609
1612
  var changes = reducer(state, action);
1610
1613
  var newState = action.props.stateReducer(state, _extends({}, action, {
1611
1614
  changes: changes
1612
1615
  }));
1613
- return _extends({}, state, newState);
1616
+ return {
1617
+ state: _extends({}, state, newState),
1618
+ lastAction: action
1619
+ };
1614
1620
  }, [reducer]);
1615
- var _React$useReducer = React.useReducer(enhancedReducer, props, createInitialState),
1616
- state = _React$useReducer[0],
1621
+ var _React$useReducer = React.useReducer(enhancedReducer, props, function (p) {
1622
+ return {
1623
+ state: createInitialState(p),
1624
+ lastAction: undefined
1625
+ };
1626
+ }),
1627
+ _React$useReducer$ = _React$useReducer[0],
1628
+ state = _React$useReducer$.state,
1629
+ lastAction = _React$useReducer$.lastAction,
1617
1630
  dispatch = _React$useReducer[1];
1618
1631
  var propsRef = useLatestRef(props);
1619
1632
  var dispatchWithProps = React.useCallback(function (action) {
@@ -1621,15 +1634,16 @@ function useEnhancedReducer(reducer, props, createInitialState, isStateEqual) {
1621
1634
  props: propsRef.current
1622
1635
  }));
1623
1636
  }, [propsRef]);
1624
- var action = actionRef.current;
1625
1637
  React.useEffect(function () {
1626
- var prevState = getState(prevStateRef.current, action == null ? void 0 : action.props);
1627
- var shouldCallOnChangeProps = action && !isStateEqual(prevState, state);
1628
- if (shouldCallOnChangeProps) {
1629
- callOnChangeProps(action, action.props, prevState, state);
1638
+ if (lastAction) {
1639
+ var prevState = getState(prevStateRef.current, lastAction.props);
1640
+ var shouldCallOnChangeProps = !isStateEqual(prevState, state);
1641
+ if (shouldCallOnChangeProps) {
1642
+ callOnChangeProps(lastAction, lastAction.props, prevState, state);
1643
+ }
1630
1644
  }
1631
1645
  prevStateRef.current = state;
1632
- }, [state, action, isStateEqual]);
1646
+ }, [state, lastAction, isStateEqual]);
1633
1647
  return [state, dispatchWithProps];
1634
1648
  }
1635
1649
 
@@ -1690,6 +1704,8 @@ function useIsInitialMount() {
1690
1704
  isInitialMountRef.current = true;
1691
1705
  };
1692
1706
  }, []);
1707
+
1708
+ // eslint-disable-next-line react-hooks/refs
1693
1709
  return isInitialMountRef.current;
1694
1710
  }
1695
1711
 
@@ -1869,8 +1885,10 @@ function useElementIdsLegacy$1(_ref2) {
1869
1885
  getItemId = _ref2.getItemId,
1870
1886
  toggleButtonId = _ref2.toggleButtonId,
1871
1887
  inputId = _ref2.inputId;
1872
- var baseIdRef = React.useRef(id != null ? id : "downshift-" + generateId());
1873
- var baseId = baseIdRef.current;
1888
+ var _React$useState = React.useState(function () {
1889
+ return id != null ? id : "downshift-" + generateId();
1890
+ }),
1891
+ baseId = _React$useState[0];
1874
1892
  var elementIds = React.useMemo(function () {
1875
1893
  return {
1876
1894
  labelId: labelId != null ? labelId : baseId + "-label",
@@ -1944,10 +1962,10 @@ function getHighlightedIndexOnOpen(items, initialHighlightedIndex, defaultHighli
1944
1962
  }
1945
1963
 
1946
1964
  // initialHighlightedIndex will give value to highlightedIndex on initial state only.
1947
- if (initialHighlightedIndex !== undefined && highlightedIndex === initialHighlightedIndex && items[initialHighlightedIndex] && !isItemDisabled(items[initialHighlightedIndex], initialHighlightedIndex)) {
1965
+ if (initialHighlightedIndex !== undefined && highlightedIndex === initialHighlightedIndex && (initialHighlightedIndex === -1 || items[initialHighlightedIndex] !== undefined && !isItemDisabled(items[initialHighlightedIndex], initialHighlightedIndex))) {
1948
1966
  return initialHighlightedIndex;
1949
1967
  }
1950
- if (defaultHighlightedIndex !== undefined && items[defaultHighlightedIndex] && !isItemDisabled(items[defaultHighlightedIndex], defaultHighlightedIndex)) {
1968
+ if (defaultHighlightedIndex !== undefined && (defaultHighlightedIndex === -1 || items[defaultHighlightedIndex] !== undefined && !isItemDisabled(items[defaultHighlightedIndex], defaultHighlightedIndex))) {
1951
1969
  return defaultHighlightedIndex;
1952
1970
  }
1953
1971
  if (selectedItem) {
@@ -2019,7 +2037,7 @@ if (process.env.NODE_ENV !== 'production') {
2019
2037
  * @param environment The environment to add the event listeners to, for instance window.
2020
2038
  * @param handleBlur The function that is called if mouseDown or touchEnd occured outside the downshiftElements.
2021
2039
  * @param downshiftRefs The refs for the elements that should not trigger a blur action from mouseDown or touchEnd.
2022
- * @returns The mouse and touch events information.
2040
+ * @returns A ref holding the mouse and touch events information. Read `.current` only inside event handlers/effects.
2023
2041
  */
2024
2042
  function useMouseAndTouchTracker(environment, handleBlur, downshiftRefs) {
2025
2043
  var mouseAndTouchTrackersRef = React.useRef({
@@ -2072,7 +2090,7 @@ function useMouseAndTouchTracker(environment, handleBlur, downshiftRefs) {
2072
2090
  environment.removeEventListener('touchend', onTouchEnd);
2073
2091
  };
2074
2092
  }, [environment, getDownshiftElements, handleBlur]);
2075
- return mouseAndTouchTrackersRef.current;
2093
+ return mouseAndTouchTrackersRef;
2076
2094
  }
2077
2095
 
2078
2096
  /* istanbul ignore next */
@@ -2127,31 +2145,28 @@ if (process.env.NODE_ENV !== 'production') {
2127
2145
  };
2128
2146
  }
2129
2147
 
2130
- // istanbul ignore next
2131
- var useIsomorphicLayoutEffect = typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined' ? React.useLayoutEffect : React.useEffect;
2132
-
2133
2148
  /**
2134
2149
  * Utility hook that scrolls an item from a menu into view.
2135
2150
  * @param scrollIntoView The function that does the scroll.
2136
2151
  * @param highlightedIndex The index of the item that should be scrolled.
2137
2152
  * @param isOpen If the menu is open or not.
2138
- * @param menuElement The menu element.
2139
- * @param itemElements The object containing item elements.
2153
+ * @param menuRef The ref to the menu element.
2154
+ * @param itemsRef The ref to the object containing item elements.
2140
2155
  * @param getItemId The function to get the item id from index.
2141
2156
  * @returns Function that when called prevents the scroll.
2142
2157
  */
2143
- function useScrollIntoView(scrollIntoView, highlightedIndex, isOpen, menuElement, itemElements, getItemId) {
2158
+ function useScrollIntoView(scrollIntoView, highlightedIndex, isOpen, menuRef, itemsRef, getItemId) {
2144
2159
  // used not to scroll on highlight by mouse.
2145
2160
  var shouldScrollRef = React.useRef(true);
2146
2161
  // Scroll on highlighted item if change comes from keyboard.
2147
2162
  useIsomorphicLayoutEffect(function () {
2148
- if (highlightedIndex < 0 || !isOpen || !Object.keys(itemElements).length) {
2163
+ if (highlightedIndex < 0 || !isOpen || !Object.keys(itemsRef.current).length) {
2149
2164
  return;
2150
2165
  }
2151
2166
  if (shouldScrollRef.current) {
2152
- var itemElement = itemElements[getItemId(highlightedIndex)];
2153
- if (itemElement && menuElement) {
2154
- scrollIntoView(itemElement, menuElement);
2167
+ var itemElement = itemsRef.current[getItemId(highlightedIndex)];
2168
+ if (itemElement && menuRef.current) {
2169
+ scrollIntoView(itemElement, menuRef.current);
2155
2170
  }
2156
2171
  } else {
2157
2172
  shouldScrollRef.current = true;
@@ -2419,7 +2434,7 @@ function downshiftSelectReducer(state, action) {
2419
2434
 
2420
2435
  var _excluded$3 = ["onClick"],
2421
2436
  _excluded2$3 = ["onMouseLeave", "refKey", "ref", "aria-label"],
2422
- _excluded3$2 = ["onBlur", "onClick", "onPress", "onKeyDown", "refKey", "ref", "disabled"],
2437
+ _excluded3$2 = ["onBlur", "onClick", "onPress", "onKeyDown", "refKey", "ref"],
2423
2438
  _excluded4$1 = ["item", "index", "onMouseMove", "onClick", "onMouseDown", "onPress", "refKey", "disabled", "ref"];
2424
2439
  useSelect.stateChangeTypes = stateChangeTypes$3;
2425
2440
  function useSelect(userProps) {
@@ -2429,7 +2444,9 @@ function useSelect(userProps) {
2429
2444
  validatePropTypes$1(userProps, useSelect, propTypes$3);
2430
2445
  // Props defaults and destructuring.
2431
2446
  var props = _extends({}, dropdownDefaultProps, userProps);
2432
- var scrollIntoView = props.scrollIntoView,
2447
+ var items = props.items,
2448
+ isItemDisabled = props.isItemDisabled,
2449
+ scrollIntoView = props.scrollIntoView,
2433
2450
  environment = props.environment,
2434
2451
  getA11yStatusMessage = props.getA11yStatusMessage;
2435
2452
  // Initial state depending on controlled props.
@@ -2450,17 +2467,18 @@ function useSelect(userProps) {
2450
2467
  var clearTimeoutRef = useRef(null);
2451
2468
  // prevent id re-generation between renders.
2452
2469
  var elementIds = useElementIds$1(props);
2453
- // utility callback to get item element.
2454
- var latest = useLatestRef({
2455
- state: state,
2456
- props: props
2457
- });
2470
+ /**
2471
+ * Ref to read `state` in handlers to preserve referential identity.
2472
+ * Only to be used in handlers and effects.
2473
+ * **never access this in getters**
2474
+ */
2475
+ var stateRef = useLatestRef(state);
2458
2476
 
2459
2477
  // Effects.
2460
2478
  // Adds an a11y aria live status message if getA11yStatusMessage is passed.
2461
2479
  useA11yMessageStatus(getA11yStatusMessage, state, [isOpen, highlightedIndex, selectedItem, inputValue], environment);
2462
2480
  // Scroll on highlighted item if change comes from keyboard.
2463
- var preventScroll = useScrollIntoView(scrollIntoView, highlightedIndex, isOpen, menuRef.current, itemsRef.current, elementIds.getItemId);
2481
+ var preventScroll = useScrollIntoView(scrollIntoView, highlightedIndex, isOpen, menuRef, itemsRef, elementIds.getItemId);
2464
2482
  // Sets cleanup for the keysSoFar callback, debounced after 500ms.
2465
2483
  useEffect(function () {
2466
2484
  // init the clean function here as we need access to dispatch.
@@ -2497,12 +2515,12 @@ function useSelect(userProps) {
2497
2515
  // eslint-disable-next-line react-hooks/exhaustive-deps
2498
2516
  }, []);
2499
2517
  var handleBlurInTracker = useCallback(function handleBlur() {
2500
- if (latest.current.state.isOpen) {
2518
+ if (stateRef.current.isOpen) {
2501
2519
  dispatch({
2502
2520
  type: ToggleButtonBlur
2503
2521
  });
2504
2522
  }
2505
- }, [dispatch, latest]);
2523
+ }, [dispatch, stateRef]);
2506
2524
  var downshiftRefs = useMemo(function () {
2507
2525
  return [menuRef, toggleButtonRef];
2508
2526
  }, []);
@@ -2545,7 +2563,7 @@ function useSelect(userProps) {
2545
2563
  });
2546
2564
  },
2547
2565
  Escape: function Escape() {
2548
- if (latest.current.state.isOpen) {
2566
+ if (stateRef.current.isOpen) {
2549
2567
  dispatch({
2550
2568
  type: ToggleButtonKeyDownEscape
2551
2569
  });
@@ -2554,11 +2572,11 @@ function useSelect(userProps) {
2554
2572
  Enter: function Enter(event) {
2555
2573
  event.preventDefault();
2556
2574
  dispatch({
2557
- type: latest.current.state.isOpen ? ToggleButtonKeyDownEnter : ToggleButtonClick$1
2575
+ type: stateRef.current.isOpen ? ToggleButtonKeyDownEnter : ToggleButtonClick$1
2558
2576
  });
2559
2577
  },
2560
2578
  PageUp: function PageUp(event) {
2561
- if (latest.current.state.isOpen) {
2579
+ if (stateRef.current.isOpen) {
2562
2580
  event.preventDefault();
2563
2581
  dispatch({
2564
2582
  type: ToggleButtonKeyDownPageUp
@@ -2566,7 +2584,7 @@ function useSelect(userProps) {
2566
2584
  }
2567
2585
  },
2568
2586
  PageDown: function PageDown(event) {
2569
- if (latest.current.state.isOpen) {
2587
+ if (stateRef.current.isOpen) {
2570
2588
  event.preventDefault();
2571
2589
  dispatch({
2572
2590
  type: ToggleButtonKeyDownPageDown
@@ -2575,7 +2593,7 @@ function useSelect(userProps) {
2575
2593
  },
2576
2594
  ' ': function _(event) {
2577
2595
  event.preventDefault();
2578
- var currentState = latest.current.state;
2596
+ var currentState = stateRef.current;
2579
2597
  if (!currentState.isOpen) {
2580
2598
  dispatch({
2581
2599
  type: ToggleButtonClick$1
@@ -2594,7 +2612,7 @@ function useSelect(userProps) {
2594
2612
  }
2595
2613
  }
2596
2614
  };
2597
- }, [dispatch, latest]);
2615
+ }, [dispatch, stateRef]);
2598
2616
 
2599
2617
  // Getter functions.
2600
2618
  var getLabelProps = useCallback(function (labelProps) {
@@ -2643,19 +2661,17 @@ function useSelect(userProps) {
2643
2661
  _ref4$refKey = _ref4.refKey,
2644
2662
  refKey = _ref4$refKey === void 0 ? 'ref' : _ref4$refKey,
2645
2663
  ref = _ref4.ref,
2646
- disabled = _ref4.disabled,
2647
2664
  rest = _objectWithoutPropertiesLoose(_ref4, _excluded3$2);
2648
2665
  var _ref5 = otherProps != null ? otherProps : {},
2649
2666
  _ref5$suppressRefErro = _ref5.suppressRefError,
2650
2667
  suppressRefError = _ref5$suppressRefErro === void 0 ? false : _ref5$suppressRefErro;
2651
- var latestState = latest.current.state;
2652
2668
  var toggleButtonHandleClick = function toggleButtonHandleClick() {
2653
2669
  dispatch({
2654
2670
  type: ToggleButtonClick$1
2655
2671
  });
2656
2672
  };
2657
2673
  var toggleButtonHandleBlur = function toggleButtonHandleBlur() {
2658
- if (latestState.isOpen && !mouseAndTouchTrackers.isMouseDown) {
2674
+ if (stateRef.current.isOpen && !mouseAndTouchTrackers.current.isMouseDown) {
2659
2675
  dispatch({
2660
2676
  type: ToggleButtonBlur
2661
2677
  });
@@ -2674,8 +2690,8 @@ function useSelect(userProps) {
2674
2690
  };
2675
2691
  var toggleProps = _extends((_extends3 = {}, _extends3[refKey] = handleRefs(ref, function (toggleButtonNode) {
2676
2692
  toggleButtonRef.current = toggleButtonNode;
2677
- }), _extends3['aria-activedescendant'] = latestState.isOpen && latestState.highlightedIndex > -1 ? elementIds.getItemId(latestState.highlightedIndex) : '', _extends3['aria-controls'] = elementIds.menuId, _extends3['aria-expanded'] = latest.current.state.isOpen, _extends3['aria-haspopup'] = 'listbox', _extends3['aria-labelledby'] = rest['aria-label'] ? undefined : "" + elementIds.labelId, _extends3.id = elementIds.toggleButtonId, _extends3.role = 'combobox', _extends3.tabIndex = 0, _extends3.onBlur = callAllEventHandlers(onBlur, toggleButtonHandleBlur), _extends3.disabled = disabled, _extends3), rest);
2678
- if (!disabled) {
2693
+ }), _extends3['aria-activedescendant'] = isOpen && highlightedIndex > -1 ? elementIds.getItemId(highlightedIndex) : '', _extends3['aria-controls'] = elementIds.menuId, _extends3['aria-expanded'] = isOpen, _extends3['aria-haspopup'] = 'listbox', _extends3['aria-labelledby'] = rest['aria-label'] ? undefined : "" + elementIds.labelId, _extends3.id = elementIds.toggleButtonId, _extends3.role = 'combobox', _extends3.tabIndex = 0, _extends3.onBlur = callAllEventHandlers(onBlur, toggleButtonHandleBlur), _extends3), rest);
2694
+ if (!rest.disabled) {
2679
2695
  /* istanbul ignore if (react-native) */
2680
2696
  {
2681
2697
  Object.assign(toggleProps, {
@@ -2686,7 +2702,7 @@ function useSelect(userProps) {
2686
2702
  }
2687
2703
  setGetterPropCallInfo('getToggleButtonProps', suppressRefError, refKey, toggleButtonRef);
2688
2704
  return toggleProps;
2689
- }, [dispatch, elementIds, latest, mouseAndTouchTrackers, setGetterPropCallInfo, toggleButtonKeyDownHandlers]);
2705
+ }, [dispatch, elementIds, isOpen, highlightedIndex, mouseAndTouchTrackers, setGetterPropCallInfo, toggleButtonKeyDownHandlers, stateRef]);
2690
2706
  var getItemProps = useCallback(function (itemProps) {
2691
2707
  var _extends4;
2692
2708
  var _ref6 = itemProps != null ? itemProps : {},
@@ -2704,15 +2720,12 @@ function useSelect(userProps) {
2704
2720
  if (disabledProp !== undefined) {
2705
2721
  console.warn('Passing "disabled" as an argument to getItemProps is not supported anymore. Please use the isItemDisabled prop from useSelect.');
2706
2722
  }
2707
- var _latest$current = latest.current,
2708
- latestState = _latest$current.state,
2709
- latestProps = _latest$current.props;
2710
- var _getItemAndIndex = getItemAndIndex(itemProp, indexProp, latestProps.items, 'Pass either item or index to getItemProps!'),
2723
+ var _getItemAndIndex = getItemAndIndex(itemProp, indexProp, items, 'Pass either item or index to getItemProps!'),
2711
2724
  item = _getItemAndIndex[0],
2712
2725
  index = _getItemAndIndex[1];
2713
- var disabled = latestProps.isItemDisabled(item, index);
2726
+ var disabled = isItemDisabled(item, index);
2714
2727
  var itemHandleMouseMove = function itemHandleMouseMove() {
2715
- if (mouseAndTouchTrackers.isTouchEnd || index === latestState.highlightedIndex) {
2728
+ if (mouseAndTouchTrackers.current.isTouchEnd || index === stateRef.current.highlightedIndex) {
2716
2729
  return;
2717
2730
  }
2718
2731
  preventScroll();
@@ -2736,7 +2749,7 @@ function useSelect(userProps) {
2736
2749
  if (itemNode) {
2737
2750
  itemsRef.current[elementIds.getItemId(index)] = itemNode;
2738
2751
  }
2739
- }), _extends4['aria-disabled'] = disabled, _extends4['aria-selected'] = item === latestState.selectedItem, _extends4.id = elementIds.getItemId(index), _extends4.role = 'option', _extends4.onMouseMove = callAllEventHandlers(onMouseMove, itemHandleMouseMove), _extends4.onMouseDown = callAllEventHandlers(onMouseDown, itemHandleMouseDown), _extends4), rest);
2752
+ }), _extends4['aria-disabled'] = disabled, _extends4['aria-selected'] = item === selectedItem, _extends4.id = elementIds.getItemId(index), _extends4.role = 'option', _extends4.onMouseMove = callAllEventHandlers(onMouseMove, itemHandleMouseMove), _extends4.onMouseDown = callAllEventHandlers(onMouseDown, itemHandleMouseDown), _extends4), rest);
2740
2753
  if (!disabled) {
2741
2754
  /* istanbul ignore next (react-native) */
2742
2755
  {
@@ -2746,7 +2759,7 @@ function useSelect(userProps) {
2746
2759
  }
2747
2760
  }
2748
2761
  return resultItemProps;
2749
- }, [latest, elementIds, mouseAndTouchTrackers, preventScroll, dispatch]);
2762
+ }, [items, isItemDisabled, selectedItem, elementIds, mouseAndTouchTrackers, preventScroll, dispatch, stateRef]);
2750
2763
 
2751
2764
  // Action functions.
2752
2765
  var toggleMenu = useCallback(function () {
@@ -3037,8 +3050,8 @@ function downshiftUseComboboxReducer(state, action) {
3037
3050
 
3038
3051
  var _excluded$2 = ["onMouseLeave", "refKey", "ref", "aria-label"],
3039
3052
  _excluded2$2 = ["item", "index", "refKey", "ref", "onMouseMove", "onMouseDown", "onClick", "onPress", "disabled"],
3040
- _excluded3$1 = ["onClick", "onPress", "refKey", "ref", "disabled"],
3041
- _excluded4 = ["aria-label", "disabled", "onKeyDown", "onChange", "onInput", "onBlur", "onChangeText", "onClick", "refKey", "ref"];
3053
+ _excluded3$1 = ["onClick", "onPress", "refKey", "ref"],
3054
+ _excluded4 = ["aria-label", "onKeyDown", "onChange", "onInput", "onBlur", "onChangeText", "onClick", "refKey", "ref"];
3042
3055
  useCombobox.stateChangeTypes = stateChangeTypes$2;
3043
3056
  function useCombobox(userProps) {
3044
3057
  if (userProps === void 0) {
@@ -3048,6 +3061,7 @@ function useCombobox(userProps) {
3048
3061
  // Props defaults and destructuring.
3049
3062
  var props = _extends({}, dropdownDefaultProps, userProps);
3050
3063
  var items = props.items,
3064
+ isItemDisabled = props.isItemDisabled,
3051
3065
  scrollIntoView = props.scrollIntoView,
3052
3066
  environment = props.environment,
3053
3067
  getA11yStatusMessage = props.getA11yStatusMessage;
@@ -3071,17 +3085,18 @@ function useCombobox(userProps) {
3071
3085
  var elementIds = useElementIds$1(props);
3072
3086
  // used to keep track of how many items we had on previous cycle.
3073
3087
  var previousResultCountRef = useRef();
3074
- // utility callback to get item element.
3075
- var latest = useLatestRef({
3076
- state: state,
3077
- props: props
3078
- });
3088
+ /**
3089
+ * Ref to read `state` in handlers to preserve referential identity.
3090
+ * Only to be used in handlers and effects.
3091
+ * **never access this in getters**
3092
+ */
3093
+ var stateRef = useLatestRef(state);
3079
3094
 
3080
3095
  // Effects.
3081
3096
  // Adds an a11y aria live status message if getA11yStatusMessage is passed.
3082
3097
  useA11yMessageStatus(getA11yStatusMessage, state, [isOpen, highlightedIndex, selectedItem, inputValue], environment);
3083
3098
  // Scroll on highlighted item if change comes from keyboard.
3084
- var preventScroll = useScrollIntoView(scrollIntoView, highlightedIndex, isOpen, menuRef.current, itemsRef.current, elementIds.getItemId);
3099
+ var preventScroll = useScrollIntoView(scrollIntoView, highlightedIndex, isOpen, menuRef, itemsRef, elementIds.getItemId);
3085
3100
  useControlPropsValidator({
3086
3101
  state: state,
3087
3102
  props: props
@@ -3100,12 +3115,12 @@ function useCombobox(userProps) {
3100
3115
  }
3101
3116
  });
3102
3117
  var handleBlurInTracker = useCallback(function handleBlur() {
3103
- if (latest.current.state.isOpen) {
3118
+ if (stateRef.current.isOpen) {
3104
3119
  dispatch({
3105
3120
  type: InputBlur
3106
3121
  });
3107
3122
  }
3108
- }, [dispatch, latest]);
3123
+ }, [dispatch, stateRef]);
3109
3124
  var downshiftRefs = useMemo(function () {
3110
3125
  return [menuRef, toggleButtonRef, inputRef];
3111
3126
  }, []);
@@ -3146,7 +3161,7 @@ function useCombobox(userProps) {
3146
3161
  });
3147
3162
  },
3148
3163
  Home: function Home(event) {
3149
- if (!latest.current.state.isOpen) {
3164
+ if (!stateRef.current.isOpen) {
3150
3165
  return;
3151
3166
  }
3152
3167
  event.preventDefault();
@@ -3155,7 +3170,7 @@ function useCombobox(userProps) {
3155
3170
  });
3156
3171
  },
3157
3172
  End: function End(event) {
3158
- if (!latest.current.state.isOpen) {
3173
+ if (!stateRef.current.isOpen) {
3159
3174
  return;
3160
3175
  }
3161
3176
  event.preventDefault();
@@ -3164,7 +3179,7 @@ function useCombobox(userProps) {
3164
3179
  });
3165
3180
  },
3166
3181
  Escape: function Escape(event) {
3167
- var latestState = latest.current.state;
3182
+ var latestState = stateRef.current;
3168
3183
  if (latestState.isOpen || latestState.inputValue || latestState.selectedItem || latestState.highlightedIndex > -1) {
3169
3184
  event.preventDefault();
3170
3185
  dispatch({
@@ -3173,7 +3188,7 @@ function useCombobox(userProps) {
3173
3188
  }
3174
3189
  },
3175
3190
  Enter: function Enter(event) {
3176
- var latestState = latest.current.state;
3191
+ var latestState = stateRef.current;
3177
3192
  // if closed or no highlighted index, do nothing.
3178
3193
  if (!latestState.isOpen || event.which === 229 // if IME composing, wait for next Enter keydown event.
3179
3194
  ) {
@@ -3185,7 +3200,7 @@ function useCombobox(userProps) {
3185
3200
  });
3186
3201
  },
3187
3202
  PageUp: function PageUp(event) {
3188
- if (latest.current.state.isOpen) {
3203
+ if (stateRef.current.isOpen) {
3189
3204
  event.preventDefault();
3190
3205
  dispatch({
3191
3206
  type: InputKeyDownPageUp
@@ -3193,7 +3208,7 @@ function useCombobox(userProps) {
3193
3208
  }
3194
3209
  },
3195
3210
  PageDown: function PageDown(event) {
3196
- if (latest.current.state.isOpen) {
3211
+ if (stateRef.current.isOpen) {
3197
3212
  event.preventDefault();
3198
3213
  dispatch({
3199
3214
  type: InputKeyDownPageDown
@@ -3201,7 +3216,7 @@ function useCombobox(userProps) {
3201
3216
  }
3202
3217
  }
3203
3218
  };
3204
- }, [dispatch, latest]);
3219
+ }, [dispatch, stateRef]);
3205
3220
 
3206
3221
  // Getter props.
3207
3222
  var getLabelProps = useCallback(function (labelProps) {
@@ -3248,17 +3263,14 @@ function useCombobox(userProps) {
3248
3263
  if (disabledProp !== undefined) {
3249
3264
  console.warn('Passing "disabled" as an argument to getItemProps is not supported anymore. Please use the isItemDisabled prop from useCombobox.');
3250
3265
  }
3251
- var _latest$current = latest.current,
3252
- latestProps = _latest$current.props,
3253
- latestState = _latest$current.state;
3254
- var _getItemAndIndex = getItemAndIndex(itemProp, indexProp, latestProps.items, 'Pass either item or index to getItemProps!'),
3266
+ var _getItemAndIndex = getItemAndIndex(itemProp, indexProp, items, 'Pass either item or index to getItemProps!'),
3255
3267
  item = _getItemAndIndex[0],
3256
3268
  index = _getItemAndIndex[1];
3257
- var disabled = latestProps.isItemDisabled(item, index);
3269
+ var disabled = isItemDisabled(item, index);
3258
3270
  var onSelectKey = 'onClick';
3259
3271
  var customClickHandler = onClick;
3260
3272
  var itemHandleMouseMove = function itemHandleMouseMove() {
3261
- if (mouseAndTouchTrackers.isTouchEnd || index === latestState.highlightedIndex) {
3273
+ if (mouseAndTouchTrackers.current.isTouchEnd || index === stateRef.current.highlightedIndex) {
3262
3274
  return;
3263
3275
  }
3264
3276
  preventScroll();
@@ -3282,11 +3294,11 @@ function useCombobox(userProps) {
3282
3294
  if (itemNode) {
3283
3295
  itemsRef.current[elementIds.getItemId(index)] = itemNode;
3284
3296
  }
3285
- }), _extends3['aria-disabled'] = disabled, _extends3['aria-selected'] = index === latestState.highlightedIndex, _extends3.id = elementIds.getItemId(index), _extends3.role = 'option', _extends3), !disabled && (_ref4 = {}, _ref4[onSelectKey] = callAllEventHandlers(customClickHandler, itemHandleClick), _ref4), {
3297
+ }), _extends3['aria-disabled'] = disabled, _extends3['aria-selected'] = index === highlightedIndex, _extends3.id = elementIds.getItemId(index), _extends3.role = 'option', _extends3), !disabled && (_ref4 = {}, _ref4[onSelectKey] = callAllEventHandlers(customClickHandler, itemHandleClick), _ref4), {
3286
3298
  onMouseMove: callAllEventHandlers(onMouseMove, itemHandleMouseMove),
3287
3299
  onMouseDown: callAllEventHandlers(onMouseDown, itemHandleMouseDown)
3288
3300
  }, rest);
3289
- }, [dispatch, elementIds, latest, mouseAndTouchTrackers, preventScroll]);
3301
+ }, [dispatch, elementIds, items, isItemDisabled, highlightedIndex, mouseAndTouchTrackers, preventScroll, stateRef]);
3290
3302
  var getToggleButtonProps = useCallback(function (toggleButtonProps) {
3291
3303
  var _extends4;
3292
3304
  var _ref5 = toggleButtonProps != null ? toggleButtonProps : {},
@@ -3295,9 +3307,7 @@ function useCombobox(userProps) {
3295
3307
  var _ref5$refKey = _ref5.refKey,
3296
3308
  refKey = _ref5$refKey === void 0 ? 'ref' : _ref5$refKey,
3297
3309
  ref = _ref5.ref,
3298
- disabled = _ref5.disabled,
3299
3310
  rest = _objectWithoutPropertiesLoose(_ref5, _excluded3$1);
3300
- var latestState = latest.current.state;
3301
3311
  var toggleButtonHandleClick = function toggleButtonHandleClick() {
3302
3312
  dispatch({
3303
3313
  type: ToggleButtonClick
@@ -3305,17 +3315,14 @@ function useCombobox(userProps) {
3305
3315
  };
3306
3316
  return _extends((_extends4 = {}, _extends4[refKey] = handleRefs(ref, function (toggleButtonNode) {
3307
3317
  toggleButtonRef.current = toggleButtonNode;
3308
- }), _extends4['aria-controls'] = elementIds.menuId, _extends4['aria-expanded'] = latestState.isOpen, _extends4.id = elementIds.toggleButtonId, _extends4.tabIndex = -1, _extends4), !disabled && _extends({}, {
3318
+ }), _extends4['aria-controls'] = elementIds.menuId, _extends4['aria-expanded'] = isOpen, _extends4.id = elementIds.toggleButtonId, _extends4.tabIndex = -1, _extends4), !rest.disabled && _extends({}, {
3309
3319
  onClick: callAllEventHandlers(onClick, toggleButtonHandleClick)
3310
- }), {
3311
- disabled: disabled
3312
- }, rest);
3313
- }, [dispatch, latest, elementIds]);
3320
+ }), rest);
3321
+ }, [dispatch, isOpen, elementIds]);
3314
3322
  var getInputProps = useCallback(function (inputProps, otherProps) {
3315
3323
  var _extends5;
3316
3324
  var _ref6 = inputProps != null ? inputProps : {},
3317
3325
  ariaLabel = _ref6['aria-label'],
3318
- disabled = _ref6.disabled,
3319
3326
  onKeyDown = _ref6.onKeyDown,
3320
3327
  onChange = _ref6.onChange,
3321
3328
  onInput = _ref6.onInput,
@@ -3330,7 +3337,6 @@ function useCombobox(userProps) {
3330
3337
  _ref7$suppressRefErro = _ref7.suppressRefError,
3331
3338
  suppressRefError = _ref7$suppressRefErro === void 0 ? false : _ref7$suppressRefErro;
3332
3339
  setGetterPropCallInfo('getInputProps', suppressRefError, refKey, inputRef);
3333
- var latestState = latest.current.state;
3334
3340
  var inputHandleKeyDown = function inputHandleKeyDown(event) {
3335
3341
  var key = normalizeArrowKey(event);
3336
3342
  if (key && key in inputKeyDownHandlers) {
@@ -3346,7 +3352,7 @@ function useCombobox(userProps) {
3346
3352
  };
3347
3353
  var inputHandleBlur = function inputHandleBlur(event) {
3348
3354
  /* istanbul ignore else */
3349
- if (environment != null && environment.document && latestState.isOpen && !mouseAndTouchTrackers.isMouseDown) {
3355
+ if (environment != null && environment.document && stateRef.current.isOpen && !mouseAndTouchTrackers.current.isMouseDown) {
3350
3356
  var isBlurByTabChange = event.relatedTarget === null && environment.document.activeElement !== environment.document.body;
3351
3357
  dispatch({
3352
3358
  type: InputBlur,
@@ -3363,14 +3369,14 @@ function useCombobox(userProps) {
3363
3369
  /* istanbul ignore next (preact) */
3364
3370
  var onChangeKey = 'onChange';
3365
3371
  var eventHandlers = {};
3366
- if (!disabled) {
3372
+ if (!rest.disabled) {
3367
3373
  var _eventHandlers;
3368
3374
  eventHandlers = (_eventHandlers = {}, _eventHandlers[onChangeKey] = callAllEventHandlers(onChange, onInput, inputHandleChange), _eventHandlers.onKeyDown = callAllEventHandlers(onKeyDown, inputHandleKeyDown), _eventHandlers.onBlur = callAllEventHandlers(onBlur, inputHandleBlur), _eventHandlers.onClick = callAllEventHandlers(onClick, inputHandleClick), _eventHandlers);
3369
3375
  }
3370
3376
  return _extends((_extends5 = {}, _extends5[refKey] = handleRefs(ref, function (inputNode) {
3371
3377
  inputRef.current = inputNode;
3372
- }), _extends5['aria-activedescendant'] = latestState.isOpen && latestState.highlightedIndex > -1 ? elementIds.getItemId(latestState.highlightedIndex) : '', _extends5['aria-autocomplete'] = 'list', _extends5['aria-controls'] = elementIds.menuId, _extends5['aria-expanded'] = latestState.isOpen, _extends5['aria-labelledby'] = ariaLabel ? undefined : elementIds.labelId, _extends5['aria-label'] = ariaLabel, _extends5.autoComplete = 'off', _extends5.disabled = disabled, _extends5.id = elementIds.inputId, _extends5.role = 'combobox', _extends5.value = latestState.inputValue, _extends5), eventHandlers, rest);
3373
- }, [dispatch, elementIds, environment, inputKeyDownHandlers, latest, mouseAndTouchTrackers, setGetterPropCallInfo]);
3378
+ }), _extends5['aria-activedescendant'] = isOpen && highlightedIndex > -1 ? elementIds.getItemId(highlightedIndex) : '', _extends5['aria-autocomplete'] = 'list', _extends5['aria-controls'] = elementIds.menuId, _extends5['aria-expanded'] = isOpen, _extends5['aria-labelledby'] = ariaLabel ? undefined : elementIds.labelId, _extends5['aria-label'] = ariaLabel, _extends5.autoComplete = 'off', _extends5.id = elementIds.inputId, _extends5.role = 'combobox', _extends5.value = inputValue, _extends5), eventHandlers, rest);
3379
+ }, [dispatch, elementIds, environment, inputKeyDownHandlers, isOpen, highlightedIndex, inputValue, mouseAndTouchTrackers, setGetterPropCallInfo, stateRef]);
3374
3380
 
3375
3381
  // returns
3376
3382
  var toggleMenu = useCallback(function () {
@@ -3692,12 +3698,13 @@ function useMultipleSelection(userProps) {
3692
3698
  // Refs.
3693
3699
  var isInitialMount = useIsInitialMount();
3694
3700
  var dropdownRef = useRef(null);
3695
- var selectedItemRefs = useRef();
3696
- selectedItemRefs.current = [];
3697
- var latest = useLatestRef({
3698
- state: state,
3699
- props: props
3700
- });
3701
+ // Map of selected-item index -> DOM node. Populated by the ref callback in
3702
+ // getSelectedItemProps and read by the focus effect. Keyed by
3703
+ // index so we never reset it during render.
3704
+ var selectedItemRefs = useRef(null);
3705
+ if (selectedItemRefs.current === null) {
3706
+ selectedItemRefs.current = new Map();
3707
+ }
3701
3708
 
3702
3709
  // Effects.
3703
3710
  // Adds an a11y aria live status message if getA11yStatusMessage is passed.
@@ -3709,8 +3716,9 @@ function useMultipleSelection(userProps) {
3709
3716
  }
3710
3717
  if (activeIndex === -1 && dropdownRef.current) {
3711
3718
  dropdownRef.current.focus();
3712
- } else if (selectedItemRefs.current[activeIndex]) {
3713
- selectedItemRefs.current[activeIndex].focus();
3719
+ } else {
3720
+ var _selectedItemRefs$cur;
3721
+ (_selectedItemRefs$cur = selectedItemRefs.current.get(activeIndex)) == null || _selectedItemRefs$cur.focus();
3714
3722
  }
3715
3723
  // eslint-disable-next-line react-hooks/exhaustive-deps
3716
3724
  }, [activeIndex]);
@@ -3770,10 +3778,9 @@ function useMultipleSelection(userProps) {
3770
3778
  selectedItemProp = _ref3.selectedItem,
3771
3779
  indexProp = _ref3.index,
3772
3780
  rest = _objectWithoutPropertiesLoose(_ref3, _excluded$1);
3773
- var latestState = latest.current.state;
3774
- var _getItemAndIndex = getItemAndIndex(selectedItemProp, indexProp, latestState.selectedItems, 'Pass either item or index to getSelectedItemProps!'),
3781
+ var _getItemAndIndex = getItemAndIndex(selectedItemProp, indexProp, selectedItems, 'Pass either item or index to getSelectedItemProps!'),
3775
3782
  index = _getItemAndIndex[1];
3776
- var isFocusable = index > -1 && index === latestState.activeIndex;
3783
+ var isFocusable = index > -1 && index === activeIndex;
3777
3784
  var selectedItemHandleClick = function selectedItemHandleClick() {
3778
3785
  dispatch({
3779
3786
  type: SelectedItemClick,
@@ -3788,10 +3795,12 @@ function useMultipleSelection(userProps) {
3788
3795
  };
3789
3796
  return _extends((_extends2 = {}, _extends2[refKey] = handleRefs(ref, function (selectedItemNode) {
3790
3797
  if (selectedItemNode) {
3791
- selectedItemRefs.current.push(selectedItemNode);
3798
+ selectedItemRefs.current.set(index, selectedItemNode);
3799
+ } else {
3800
+ selectedItemRefs.current["delete"](index);
3792
3801
  }
3793
3802
  }), _extends2.tabIndex = isFocusable ? 0 : -1, _extends2.onClick = callAllEventHandlers(onClick, selectedItemHandleClick), _extends2.onKeyDown = callAllEventHandlers(onKeyDown, selectedItemHandleKeyDown), _extends2), rest);
3794
- }, [dispatch, latest, selectedItemKeyDownHandlers]);
3803
+ }, [dispatch, selectedItems, activeIndex, selectedItemKeyDownHandlers]);
3795
3804
  var getDropdownProps = useCallback(function (_temp2, _temp3) {
3796
3805
  var _extends3;
3797
3806
  var _ref4 = _temp2 === void 0 ? {} : _temp2,
@@ -3979,8 +3988,10 @@ function useElementIdsLegacy(_ref2) {
3979
3988
  var id = _ref2.id,
3980
3989
  getTagId = _ref2.getTagId,
3981
3990
  tagGroupId = _ref2.tagGroupId;
3982
- var baseIdRef = React.useRef(id != null ? id : "downshift-" + generateId());
3983
- var baseId = baseIdRef.current;
3991
+ var _React$useState = React.useState(function () {
3992
+ return id != null ? id : "downshift-" + generateId();
3993
+ }),
3994
+ baseId = _React$useState[0];
3984
3995
  var elementIds = React.useMemo(function () {
3985
3996
  return {
3986
3997
  tagGroupId: tagGroupId != null ? tagGroupId : baseId + "-tag-group",
@@ -4077,10 +4088,6 @@ var _useTagGroup = function useTagGroup(userProps) {
4077
4088
 
4078
4089
  /* Refs */
4079
4090
 
4080
- var latest = useLatestRef({
4081
- state: state,
4082
- props: props
4083
- });
4084
4091
  var elementIds = useElementIds({
4085
4092
  getTagId: props.getTagId,
4086
4093
  id: props.id,
@@ -4143,7 +4150,6 @@ var _useTagGroup = function useTagGroup(userProps) {
4143
4150
  if (!Number.isInteger(index) || index < 0) {
4144
4151
  throw new Error('Pass correct item index to getTagProps!');
4145
4152
  }
4146
- var latestState = latest.current.state;
4147
4153
  var handleClick = function handleClick() {
4148
4154
  dispatch({
4149
4155
  type: TagClick,
@@ -4157,8 +4163,8 @@ var _useTagGroup = function useTagGroup(userProps) {
4157
4163
  if (itemNode) {
4158
4164
  itemRefs.current[tagId] = itemNode;
4159
4165
  }
4160
- }), _extends2['aria-labelledby'] = tagId, _extends2.role = 'option', _extends2.id = tagId, _extends2.onClick = callAllEventHandlers(onClick, handleClick), _extends2.tabIndex = latestState.activeIndex === index ? 0 : -1, _extends2), rest);
4161
- }, [dispatch, elementIds, latest, itemRefs]);
4166
+ }), _extends2['aria-labelledby'] = tagId, _extends2.role = 'option', _extends2.id = tagId, _extends2.onClick = callAllEventHandlers(onClick, handleClick), _extends2.tabIndex = activeIndex === index ? 0 : -1, _extends2), rest);
4167
+ }, [dispatch, elementIds, activeIndex, itemRefs]);
4162
4168
  var getTagRemoveProps = useCallback(function (options) {
4163
4169
  var index = options.index,
4164
4170
  onClick = options.onClick,