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
@@ -90,14 +90,15 @@ function resetIdCounter() {
90
90
  idCounter = 0;
91
91
  }
92
92
 
93
- function useLatestRef(val) {
94
- var ref = React__namespace.useRef(val);
95
- // technically this is not "concurrent mode safe" because we're manipulating
96
- // the value during render (so it's not idempotent). However, the places this
97
- // hook is used is to support memoizing callbacks which will be called
98
- // *during* render, so we need the latest values *during* render.
99
- // If not for this, then we'd probably want to use useLayoutEffect instead.
100
- ref.current = val;
93
+ // React Native supports layout effects even though it does not have a DOM.
94
+ // istanbul ignore next
95
+ var useIsomorphicLayoutEffect = React__namespace.useLayoutEffect ;
96
+
97
+ function useLatestRef(value) {
98
+ var ref = React__namespace.useRef(value);
99
+ useIsomorphicLayoutEffect(function () {
100
+ ref.current = value;
101
+ }, [value]);
101
102
  return ref;
102
103
  }
103
104
 
@@ -1514,18 +1515,27 @@ function invokeOnChangeHandler(key, action, props, state, newState) {
1514
1515
  */
1515
1516
  function useEnhancedReducer(reducer, props, createInitialState, isStateEqual) {
1516
1517
  var prevStateRef = React__namespace.useRef({});
1517
- var actionRef = React__namespace.useRef();
1518
- var enhancedReducer = React__namespace.useCallback(function (state, action) {
1519
- actionRef.current = action;
1518
+ var enhancedReducer = React__namespace.useCallback(function (_ref, action) {
1519
+ var state = _ref.state;
1520
1520
  state = getState(state, action.props);
1521
1521
  var changes = reducer(state, action);
1522
1522
  var newState = action.props.stateReducer(state, _extends({}, action, {
1523
1523
  changes: changes
1524
1524
  }));
1525
- return _extends({}, state, newState);
1525
+ return {
1526
+ state: _extends({}, state, newState),
1527
+ lastAction: action
1528
+ };
1526
1529
  }, [reducer]);
1527
- var _React$useReducer = React__namespace.useReducer(enhancedReducer, props, createInitialState),
1528
- state = _React$useReducer[0],
1530
+ var _React$useReducer = React__namespace.useReducer(enhancedReducer, props, function (p) {
1531
+ return {
1532
+ state: createInitialState(p),
1533
+ lastAction: undefined
1534
+ };
1535
+ }),
1536
+ _React$useReducer$ = _React$useReducer[0],
1537
+ state = _React$useReducer$.state,
1538
+ lastAction = _React$useReducer$.lastAction,
1529
1539
  dispatch = _React$useReducer[1];
1530
1540
  var propsRef = useLatestRef(props);
1531
1541
  var dispatchWithProps = React__namespace.useCallback(function (action) {
@@ -1533,15 +1543,16 @@ function useEnhancedReducer(reducer, props, createInitialState, isStateEqual) {
1533
1543
  props: propsRef.current
1534
1544
  }));
1535
1545
  }, [propsRef]);
1536
- var action = actionRef.current;
1537
1546
  React__namespace.useEffect(function () {
1538
- var prevState = getState(prevStateRef.current, action == null ? void 0 : action.props);
1539
- var shouldCallOnChangeProps = action && !isStateEqual(prevState, state);
1540
- if (shouldCallOnChangeProps) {
1541
- callOnChangeProps(action, action.props, prevState, state);
1547
+ if (lastAction) {
1548
+ var prevState = getState(prevStateRef.current, lastAction.props);
1549
+ var shouldCallOnChangeProps = !isStateEqual(prevState, state);
1550
+ if (shouldCallOnChangeProps) {
1551
+ callOnChangeProps(lastAction, lastAction.props, prevState, state);
1552
+ }
1542
1553
  }
1543
1554
  prevStateRef.current = state;
1544
- }, [state, action, isStateEqual]);
1555
+ }, [state, lastAction, isStateEqual]);
1545
1556
  return [state, dispatchWithProps];
1546
1557
  }
1547
1558
 
@@ -1602,6 +1613,8 @@ function useIsInitialMount() {
1602
1613
  isInitialMountRef.current = true;
1603
1614
  };
1604
1615
  }, []);
1616
+
1617
+ // eslint-disable-next-line react-hooks/refs
1605
1618
  return isInitialMountRef.current;
1606
1619
  }
1607
1620
 
@@ -1779,8 +1792,10 @@ function useElementIdsLegacy$1(_ref2) {
1779
1792
  getItemId = _ref2.getItemId,
1780
1793
  toggleButtonId = _ref2.toggleButtonId,
1781
1794
  inputId = _ref2.inputId;
1782
- var baseIdRef = React__namespace.useRef(id != null ? id : "downshift-" + generateId());
1783
- var baseId = baseIdRef.current;
1795
+ var _React$useState = React__namespace.useState(function () {
1796
+ return id != null ? id : "downshift-" + generateId();
1797
+ }),
1798
+ baseId = _React$useState[0];
1784
1799
  var elementIds = React__namespace.useMemo(function () {
1785
1800
  return {
1786
1801
  labelId: labelId != null ? labelId : baseId + "-label",
@@ -1854,10 +1869,10 @@ function getHighlightedIndexOnOpen(items, initialHighlightedIndex, defaultHighli
1854
1869
  }
1855
1870
 
1856
1871
  // initialHighlightedIndex will give value to highlightedIndex on initial state only.
1857
- if (initialHighlightedIndex !== undefined && highlightedIndex === initialHighlightedIndex && items[initialHighlightedIndex] && !isItemDisabled(items[initialHighlightedIndex], initialHighlightedIndex)) {
1872
+ if (initialHighlightedIndex !== undefined && highlightedIndex === initialHighlightedIndex && (initialHighlightedIndex === -1 || items[initialHighlightedIndex] !== undefined && !isItemDisabled(items[initialHighlightedIndex], initialHighlightedIndex))) {
1858
1873
  return initialHighlightedIndex;
1859
1874
  }
1860
- if (defaultHighlightedIndex !== undefined && items[defaultHighlightedIndex] && !isItemDisabled(items[defaultHighlightedIndex], defaultHighlightedIndex)) {
1875
+ if (defaultHighlightedIndex !== undefined && (defaultHighlightedIndex === -1 || items[defaultHighlightedIndex] !== undefined && !isItemDisabled(items[defaultHighlightedIndex], defaultHighlightedIndex))) {
1861
1876
  return defaultHighlightedIndex;
1862
1877
  }
1863
1878
  if (selectedItem) {
@@ -1929,7 +1944,7 @@ if (process.env.NODE_ENV !== 'production') {
1929
1944
  * @param environment The environment to add the event listeners to, for instance window.
1930
1945
  * @param handleBlur The function that is called if mouseDown or touchEnd occured outside the downshiftElements.
1931
1946
  * @param downshiftRefs The refs for the elements that should not trigger a blur action from mouseDown or touchEnd.
1932
- * @returns The mouse and touch events information.
1947
+ * @returns A ref holding the mouse and touch events information. Read `.current` only inside event handlers/effects.
1933
1948
  */
1934
1949
  function useMouseAndTouchTracker(environment, handleBlur, downshiftRefs) {
1935
1950
  var mouseAndTouchTrackersRef = React__namespace.useRef({
@@ -1947,7 +1962,7 @@ function useMouseAndTouchTracker(environment, handleBlur, downshiftRefs) {
1947
1962
  return noop;
1948
1963
  }
1949
1964
  }, [environment, getDownshiftElements, handleBlur]);
1950
- return mouseAndTouchTrackersRef.current;
1965
+ return mouseAndTouchTrackersRef;
1951
1966
  }
1952
1967
 
1953
1968
  /* istanbul ignore next */
@@ -2002,31 +2017,28 @@ if (process.env.NODE_ENV !== 'production') {
2002
2017
  };
2003
2018
  }
2004
2019
 
2005
- // istanbul ignore next
2006
- var useIsomorphicLayoutEffect = typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined' ? React__namespace.useLayoutEffect : React__namespace.useEffect;
2007
-
2008
2020
  /**
2009
2021
  * Utility hook that scrolls an item from a menu into view.
2010
2022
  * @param scrollIntoView The function that does the scroll.
2011
2023
  * @param highlightedIndex The index of the item that should be scrolled.
2012
2024
  * @param isOpen If the menu is open or not.
2013
- * @param menuElement The menu element.
2014
- * @param itemElements The object containing item elements.
2025
+ * @param menuRef The ref to the menu element.
2026
+ * @param itemsRef The ref to the object containing item elements.
2015
2027
  * @param getItemId The function to get the item id from index.
2016
2028
  * @returns Function that when called prevents the scroll.
2017
2029
  */
2018
- function useScrollIntoView(scrollIntoView, highlightedIndex, isOpen, menuElement, itemElements, getItemId) {
2030
+ function useScrollIntoView(scrollIntoView, highlightedIndex, isOpen, menuRef, itemsRef, getItemId) {
2019
2031
  // used not to scroll on highlight by mouse.
2020
2032
  var shouldScrollRef = React__namespace.useRef(true);
2021
2033
  // Scroll on highlighted item if change comes from keyboard.
2022
2034
  useIsomorphicLayoutEffect(function () {
2023
- if (highlightedIndex < 0 || !isOpen || !Object.keys(itemElements).length) {
2035
+ if (highlightedIndex < 0 || !isOpen || !Object.keys(itemsRef.current).length) {
2024
2036
  return;
2025
2037
  }
2026
2038
  if (shouldScrollRef.current) {
2027
- var itemElement = itemElements[getItemId(highlightedIndex)];
2028
- if (itemElement && menuElement) {
2029
- scrollIntoView(itemElement, menuElement);
2039
+ var itemElement = itemsRef.current[getItemId(highlightedIndex)];
2040
+ if (itemElement && menuRef.current) {
2041
+ scrollIntoView(itemElement, menuRef.current);
2030
2042
  }
2031
2043
  } else {
2032
2044
  shouldScrollRef.current = true;
@@ -2294,7 +2306,7 @@ function downshiftSelectReducer(state, action) {
2294
2306
 
2295
2307
  var _excluded$3 = ["onClick"],
2296
2308
  _excluded2$3 = ["onMouseLeave", "refKey", "ref", "aria-label"],
2297
- _excluded3$2 = ["onBlur", "onClick", "onPress", "onKeyDown", "refKey", "ref", "disabled"],
2309
+ _excluded3$2 = ["onBlur", "onClick", "onPress", "onKeyDown", "refKey", "ref"],
2298
2310
  _excluded4$1 = ["item", "index", "onMouseMove", "onClick", "onMouseDown", "onPress", "refKey", "disabled", "ref"];
2299
2311
  useSelect.stateChangeTypes = stateChangeTypes$3;
2300
2312
  function useSelect(userProps) {
@@ -2304,7 +2316,9 @@ function useSelect(userProps) {
2304
2316
  validatePropTypes$1(userProps, useSelect, propTypes$3);
2305
2317
  // Props defaults and destructuring.
2306
2318
  var props = _extends({}, dropdownDefaultProps, userProps);
2307
- var scrollIntoView = props.scrollIntoView,
2319
+ var items = props.items,
2320
+ isItemDisabled = props.isItemDisabled,
2321
+ scrollIntoView = props.scrollIntoView,
2308
2322
  environment = props.environment,
2309
2323
  getA11yStatusMessage = props.getA11yStatusMessage;
2310
2324
  // Initial state depending on controlled props.
@@ -2325,17 +2339,18 @@ function useSelect(userProps) {
2325
2339
  var clearTimeoutRef = React.useRef(null);
2326
2340
  // prevent id re-generation between renders.
2327
2341
  var elementIds = useElementIds$1(props);
2328
- // utility callback to get item element.
2329
- var latest = useLatestRef({
2330
- state: state,
2331
- props: props
2332
- });
2342
+ /**
2343
+ * Ref to read `state` in handlers to preserve referential identity.
2344
+ * Only to be used in handlers and effects.
2345
+ * **never access this in getters**
2346
+ */
2347
+ var stateRef = useLatestRef(state);
2333
2348
 
2334
2349
  // Effects.
2335
2350
  // Adds an a11y aria live status message if getA11yStatusMessage is passed.
2336
2351
  useA11yMessageStatus(getA11yStatusMessage, state, [isOpen, highlightedIndex, selectedItem, inputValue], environment);
2337
2352
  // Scroll on highlighted item if change comes from keyboard.
2338
- var preventScroll = useScrollIntoView(scrollIntoView, highlightedIndex, isOpen, menuRef.current, itemsRef.current, elementIds.getItemId);
2353
+ var preventScroll = useScrollIntoView(scrollIntoView, highlightedIndex, isOpen, menuRef, itemsRef, elementIds.getItemId);
2339
2354
  // Sets cleanup for the keysSoFar callback, debounced after 500ms.
2340
2355
  React.useEffect(function () {
2341
2356
  // init the clean function here as we need access to dispatch.
@@ -2372,12 +2387,12 @@ function useSelect(userProps) {
2372
2387
  // eslint-disable-next-line react-hooks/exhaustive-deps
2373
2388
  }, []);
2374
2389
  var handleBlurInTracker = React.useCallback(function handleBlur() {
2375
- if (latest.current.state.isOpen) {
2390
+ if (stateRef.current.isOpen) {
2376
2391
  dispatch({
2377
2392
  type: ToggleButtonBlur
2378
2393
  });
2379
2394
  }
2380
- }, [dispatch, latest]);
2395
+ }, [dispatch, stateRef]);
2381
2396
  var downshiftRefs = React.useMemo(function () {
2382
2397
  return [menuRef, toggleButtonRef];
2383
2398
  }, []);
@@ -2420,7 +2435,7 @@ function useSelect(userProps) {
2420
2435
  });
2421
2436
  },
2422
2437
  Escape: function Escape() {
2423
- if (latest.current.state.isOpen) {
2438
+ if (stateRef.current.isOpen) {
2424
2439
  dispatch({
2425
2440
  type: ToggleButtonKeyDownEscape
2426
2441
  });
@@ -2429,11 +2444,11 @@ function useSelect(userProps) {
2429
2444
  Enter: function Enter(event) {
2430
2445
  event.preventDefault();
2431
2446
  dispatch({
2432
- type: latest.current.state.isOpen ? ToggleButtonKeyDownEnter : ToggleButtonClick$1
2447
+ type: stateRef.current.isOpen ? ToggleButtonKeyDownEnter : ToggleButtonClick$1
2433
2448
  });
2434
2449
  },
2435
2450
  PageUp: function PageUp(event) {
2436
- if (latest.current.state.isOpen) {
2451
+ if (stateRef.current.isOpen) {
2437
2452
  event.preventDefault();
2438
2453
  dispatch({
2439
2454
  type: ToggleButtonKeyDownPageUp
@@ -2441,7 +2456,7 @@ function useSelect(userProps) {
2441
2456
  }
2442
2457
  },
2443
2458
  PageDown: function PageDown(event) {
2444
- if (latest.current.state.isOpen) {
2459
+ if (stateRef.current.isOpen) {
2445
2460
  event.preventDefault();
2446
2461
  dispatch({
2447
2462
  type: ToggleButtonKeyDownPageDown
@@ -2450,7 +2465,7 @@ function useSelect(userProps) {
2450
2465
  },
2451
2466
  ' ': function _(event) {
2452
2467
  event.preventDefault();
2453
- var currentState = latest.current.state;
2468
+ var currentState = stateRef.current;
2454
2469
  if (!currentState.isOpen) {
2455
2470
  dispatch({
2456
2471
  type: ToggleButtonClick$1
@@ -2469,7 +2484,7 @@ function useSelect(userProps) {
2469
2484
  }
2470
2485
  }
2471
2486
  };
2472
- }, [dispatch, latest]);
2487
+ }, [dispatch, stateRef]);
2473
2488
 
2474
2489
  // Getter functions.
2475
2490
  var getLabelProps = React.useCallback(function (labelProps) {
@@ -2518,19 +2533,17 @@ function useSelect(userProps) {
2518
2533
  var _ref4$refKey = _ref4.refKey,
2519
2534
  refKey = _ref4$refKey === void 0 ? 'ref' : _ref4$refKey,
2520
2535
  ref = _ref4.ref,
2521
- disabled = _ref4.disabled,
2522
2536
  rest = _objectWithoutPropertiesLoose(_ref4, _excluded3$2);
2523
2537
  var _ref5 = otherProps != null ? otherProps : {},
2524
2538
  _ref5$suppressRefErro = _ref5.suppressRefError,
2525
2539
  suppressRefError = _ref5$suppressRefErro === void 0 ? false : _ref5$suppressRefErro;
2526
- var latestState = latest.current.state;
2527
2540
  var toggleButtonHandleClick = function toggleButtonHandleClick() {
2528
2541
  dispatch({
2529
2542
  type: ToggleButtonClick$1
2530
2543
  });
2531
2544
  };
2532
2545
  var toggleButtonHandleBlur = function toggleButtonHandleBlur() {
2533
- if (latestState.isOpen && !mouseAndTouchTrackers.isMouseDown) {
2546
+ if (stateRef.current.isOpen && !mouseAndTouchTrackers.current.isMouseDown) {
2534
2547
  dispatch({
2535
2548
  type: ToggleButtonBlur
2536
2549
  });
@@ -2538,8 +2551,8 @@ function useSelect(userProps) {
2538
2551
  };
2539
2552
  var toggleProps = _extends((_extends3 = {}, _extends3[refKey] = handleRefs(ref, function (toggleButtonNode) {
2540
2553
  toggleButtonRef.current = toggleButtonNode;
2541
- }), _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);
2542
- if (!disabled) {
2554
+ }), _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);
2555
+ if (!rest.disabled) {
2543
2556
  /* istanbul ignore if (react-native) */
2544
2557
  {
2545
2558
  Object.assign(toggleProps, {
@@ -2549,7 +2562,7 @@ function useSelect(userProps) {
2549
2562
  }
2550
2563
  setGetterPropCallInfo('getToggleButtonProps', suppressRefError, refKey, toggleButtonRef);
2551
2564
  return toggleProps;
2552
- }, [dispatch, elementIds, latest, mouseAndTouchTrackers, setGetterPropCallInfo, toggleButtonKeyDownHandlers]);
2565
+ }, [dispatch, elementIds, isOpen, highlightedIndex, mouseAndTouchTrackers, setGetterPropCallInfo, toggleButtonKeyDownHandlers, stateRef]);
2553
2566
  var getItemProps = React.useCallback(function (itemProps) {
2554
2567
  var _extends4;
2555
2568
  var _ref6 = itemProps != null ? itemProps : {},
@@ -2567,15 +2580,12 @@ function useSelect(userProps) {
2567
2580
  if (disabledProp !== undefined) {
2568
2581
  console.warn('Passing "disabled" as an argument to getItemProps is not supported anymore. Please use the isItemDisabled prop from useSelect.');
2569
2582
  }
2570
- var _latest$current = latest.current,
2571
- latestState = _latest$current.state,
2572
- latestProps = _latest$current.props;
2573
- var _getItemAndIndex = getItemAndIndex(itemProp, indexProp, latestProps.items, 'Pass either item or index to getItemProps!'),
2583
+ var _getItemAndIndex = getItemAndIndex(itemProp, indexProp, items, 'Pass either item or index to getItemProps!'),
2574
2584
  item = _getItemAndIndex[0],
2575
2585
  index = _getItemAndIndex[1];
2576
- var disabled = latestProps.isItemDisabled(item, index);
2586
+ var disabled = isItemDisabled(item, index);
2577
2587
  var itemHandleMouseMove = function itemHandleMouseMove() {
2578
- if (mouseAndTouchTrackers.isTouchEnd || index === latestState.highlightedIndex) {
2588
+ if (mouseAndTouchTrackers.current.isTouchEnd || index === stateRef.current.highlightedIndex) {
2579
2589
  return;
2580
2590
  }
2581
2591
  preventScroll();
@@ -2599,7 +2609,7 @@ function useSelect(userProps) {
2599
2609
  if (itemNode) {
2600
2610
  itemsRef.current[elementIds.getItemId(index)] = itemNode;
2601
2611
  }
2602
- }), _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);
2612
+ }), _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);
2603
2613
  if (!disabled) {
2604
2614
  /* istanbul ignore next (react-native) */
2605
2615
  {
@@ -2609,7 +2619,7 @@ function useSelect(userProps) {
2609
2619
  }
2610
2620
  }
2611
2621
  return resultItemProps;
2612
- }, [latest, elementIds, mouseAndTouchTrackers, preventScroll, dispatch]);
2622
+ }, [items, isItemDisabled, selectedItem, elementIds, mouseAndTouchTrackers, preventScroll, dispatch, stateRef]);
2613
2623
 
2614
2624
  // Action functions.
2615
2625
  var toggleMenu = React.useCallback(function () {
@@ -2900,8 +2910,8 @@ function downshiftUseComboboxReducer(state, action) {
2900
2910
 
2901
2911
  var _excluded$2 = ["onMouseLeave", "refKey", "ref", "aria-label"],
2902
2912
  _excluded2$2 = ["item", "index", "refKey", "ref", "onMouseMove", "onMouseDown", "onClick", "onPress", "disabled"],
2903
- _excluded3$1 = ["onClick", "onPress", "refKey", "ref", "disabled"],
2904
- _excluded4 = ["aria-label", "disabled", "onKeyDown", "onChange", "onInput", "onBlur", "onChangeText", "onClick", "refKey", "ref"];
2913
+ _excluded3$1 = ["onClick", "onPress", "refKey", "ref"],
2914
+ _excluded4 = ["aria-label", "onKeyDown", "onChange", "onInput", "onBlur", "onChangeText", "onClick", "refKey", "ref"];
2905
2915
  useCombobox.stateChangeTypes = stateChangeTypes$2;
2906
2916
  function useCombobox(userProps) {
2907
2917
  if (userProps === void 0) {
@@ -2911,6 +2921,7 @@ function useCombobox(userProps) {
2911
2921
  // Props defaults and destructuring.
2912
2922
  var props = _extends({}, dropdownDefaultProps, userProps);
2913
2923
  var items = props.items,
2924
+ isItemDisabled = props.isItemDisabled,
2914
2925
  scrollIntoView = props.scrollIntoView,
2915
2926
  environment = props.environment,
2916
2927
  getA11yStatusMessage = props.getA11yStatusMessage;
@@ -2934,17 +2945,18 @@ function useCombobox(userProps) {
2934
2945
  var elementIds = useElementIds$1(props);
2935
2946
  // used to keep track of how many items we had on previous cycle.
2936
2947
  var previousResultCountRef = React.useRef();
2937
- // utility callback to get item element.
2938
- var latest = useLatestRef({
2939
- state: state,
2940
- props: props
2941
- });
2948
+ /**
2949
+ * Ref to read `state` in handlers to preserve referential identity.
2950
+ * Only to be used in handlers and effects.
2951
+ * **never access this in getters**
2952
+ */
2953
+ var stateRef = useLatestRef(state);
2942
2954
 
2943
2955
  // Effects.
2944
2956
  // Adds an a11y aria live status message if getA11yStatusMessage is passed.
2945
2957
  useA11yMessageStatus(getA11yStatusMessage, state, [isOpen, highlightedIndex, selectedItem, inputValue], environment);
2946
2958
  // Scroll on highlighted item if change comes from keyboard.
2947
- var preventScroll = useScrollIntoView(scrollIntoView, highlightedIndex, isOpen, menuRef.current, itemsRef.current, elementIds.getItemId);
2959
+ var preventScroll = useScrollIntoView(scrollIntoView, highlightedIndex, isOpen, menuRef, itemsRef, elementIds.getItemId);
2948
2960
  useControlPropsValidator({
2949
2961
  state: state,
2950
2962
  props: props
@@ -2963,12 +2975,12 @@ function useCombobox(userProps) {
2963
2975
  }
2964
2976
  });
2965
2977
  var handleBlurInTracker = React.useCallback(function handleBlur() {
2966
- if (latest.current.state.isOpen) {
2978
+ if (stateRef.current.isOpen) {
2967
2979
  dispatch({
2968
2980
  type: InputBlur
2969
2981
  });
2970
2982
  }
2971
- }, [dispatch, latest]);
2983
+ }, [dispatch, stateRef]);
2972
2984
  var downshiftRefs = React.useMemo(function () {
2973
2985
  return [menuRef, toggleButtonRef, inputRef];
2974
2986
  }, []);
@@ -3009,7 +3021,7 @@ function useCombobox(userProps) {
3009
3021
  });
3010
3022
  },
3011
3023
  Home: function Home(event) {
3012
- if (!latest.current.state.isOpen) {
3024
+ if (!stateRef.current.isOpen) {
3013
3025
  return;
3014
3026
  }
3015
3027
  event.preventDefault();
@@ -3018,7 +3030,7 @@ function useCombobox(userProps) {
3018
3030
  });
3019
3031
  },
3020
3032
  End: function End(event) {
3021
- if (!latest.current.state.isOpen) {
3033
+ if (!stateRef.current.isOpen) {
3022
3034
  return;
3023
3035
  }
3024
3036
  event.preventDefault();
@@ -3027,7 +3039,7 @@ function useCombobox(userProps) {
3027
3039
  });
3028
3040
  },
3029
3041
  Escape: function Escape(event) {
3030
- var latestState = latest.current.state;
3042
+ var latestState = stateRef.current;
3031
3043
  if (latestState.isOpen || latestState.inputValue || latestState.selectedItem || latestState.highlightedIndex > -1) {
3032
3044
  event.preventDefault();
3033
3045
  dispatch({
@@ -3036,7 +3048,7 @@ function useCombobox(userProps) {
3036
3048
  }
3037
3049
  },
3038
3050
  Enter: function Enter(event) {
3039
- var latestState = latest.current.state;
3051
+ var latestState = stateRef.current;
3040
3052
  // if closed or no highlighted index, do nothing.
3041
3053
  if (!latestState.isOpen || event.which === 229 // if IME composing, wait for next Enter keydown event.
3042
3054
  ) {
@@ -3048,7 +3060,7 @@ function useCombobox(userProps) {
3048
3060
  });
3049
3061
  },
3050
3062
  PageUp: function PageUp(event) {
3051
- if (latest.current.state.isOpen) {
3063
+ if (stateRef.current.isOpen) {
3052
3064
  event.preventDefault();
3053
3065
  dispatch({
3054
3066
  type: InputKeyDownPageUp
@@ -3056,7 +3068,7 @@ function useCombobox(userProps) {
3056
3068
  }
3057
3069
  },
3058
3070
  PageDown: function PageDown(event) {
3059
- if (latest.current.state.isOpen) {
3071
+ if (stateRef.current.isOpen) {
3060
3072
  event.preventDefault();
3061
3073
  dispatch({
3062
3074
  type: InputKeyDownPageDown
@@ -3064,7 +3076,7 @@ function useCombobox(userProps) {
3064
3076
  }
3065
3077
  }
3066
3078
  };
3067
- }, [dispatch, latest]);
3079
+ }, [dispatch, stateRef]);
3068
3080
 
3069
3081
  // Getter props.
3070
3082
  var getLabelProps = React.useCallback(function (labelProps) {
@@ -3111,17 +3123,14 @@ function useCombobox(userProps) {
3111
3123
  if (disabledProp !== undefined) {
3112
3124
  console.warn('Passing "disabled" as an argument to getItemProps is not supported anymore. Please use the isItemDisabled prop from useCombobox.');
3113
3125
  }
3114
- var _latest$current = latest.current,
3115
- latestProps = _latest$current.props,
3116
- latestState = _latest$current.state;
3117
- var _getItemAndIndex = getItemAndIndex(itemProp, indexProp, latestProps.items, 'Pass either item or index to getItemProps!'),
3126
+ var _getItemAndIndex = getItemAndIndex(itemProp, indexProp, items, 'Pass either item or index to getItemProps!'),
3118
3127
  item = _getItemAndIndex[0],
3119
3128
  index = _getItemAndIndex[1];
3120
- var disabled = latestProps.isItemDisabled(item, index);
3129
+ var disabled = isItemDisabled(item, index);
3121
3130
  var onSelectKey = /* istanbul ignore next (react-native) */'onPress' ;
3122
3131
  var customClickHandler = /* istanbul ignore next (react-native) */onPress ;
3123
3132
  var itemHandleMouseMove = function itemHandleMouseMove() {
3124
- if (mouseAndTouchTrackers.isTouchEnd || index === latestState.highlightedIndex) {
3133
+ if (mouseAndTouchTrackers.current.isTouchEnd || index === stateRef.current.highlightedIndex) {
3125
3134
  return;
3126
3135
  }
3127
3136
  preventScroll();
@@ -3145,11 +3154,11 @@ function useCombobox(userProps) {
3145
3154
  if (itemNode) {
3146
3155
  itemsRef.current[elementIds.getItemId(index)] = itemNode;
3147
3156
  }
3148
- }), _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), {
3157
+ }), _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), {
3149
3158
  onMouseMove: callAllEventHandlers(onMouseMove, itemHandleMouseMove),
3150
3159
  onMouseDown: callAllEventHandlers(onMouseDown, itemHandleMouseDown)
3151
3160
  }, rest);
3152
- }, [dispatch, elementIds, latest, mouseAndTouchTrackers, preventScroll]);
3161
+ }, [dispatch, elementIds, items, isItemDisabled, highlightedIndex, mouseAndTouchTrackers, preventScroll, stateRef]);
3153
3162
  var getToggleButtonProps = React.useCallback(function (toggleButtonProps) {
3154
3163
  var _extends4;
3155
3164
  var _ref5 = toggleButtonProps != null ? toggleButtonProps : {};
@@ -3158,9 +3167,7 @@ function useCombobox(userProps) {
3158
3167
  _ref5$refKey = _ref5.refKey,
3159
3168
  refKey = _ref5$refKey === void 0 ? 'ref' : _ref5$refKey,
3160
3169
  ref = _ref5.ref,
3161
- disabled = _ref5.disabled,
3162
3170
  rest = _objectWithoutPropertiesLoose(_ref5, _excluded3$1);
3163
- var latestState = latest.current.state;
3164
3171
  var toggleButtonHandleClick = function toggleButtonHandleClick() {
3165
3172
  dispatch({
3166
3173
  type: ToggleButtonClick
@@ -3168,17 +3175,14 @@ function useCombobox(userProps) {
3168
3175
  };
3169
3176
  return _extends((_extends4 = {}, _extends4[refKey] = handleRefs(ref, function (toggleButtonNode) {
3170
3177
  toggleButtonRef.current = toggleButtonNode;
3171
- }), _extends4['aria-controls'] = elementIds.menuId, _extends4['aria-expanded'] = latestState.isOpen, _extends4.id = elementIds.toggleButtonId, _extends4.tabIndex = -1, _extends4), !disabled && _extends({}, /* istanbul ignore next (react-native) */{
3178
+ }), _extends4['aria-controls'] = elementIds.menuId, _extends4['aria-expanded'] = isOpen, _extends4.id = elementIds.toggleButtonId, _extends4.tabIndex = -1, _extends4), !rest.disabled && _extends({}, /* istanbul ignore next (react-native) */{
3172
3179
  onPress: callAllEventHandlers(onPress, toggleButtonHandleClick)
3173
- } ), {
3174
- disabled: disabled
3175
- }, rest);
3176
- }, [dispatch, latest, elementIds]);
3180
+ } ), rest);
3181
+ }, [dispatch, isOpen, elementIds]);
3177
3182
  var getInputProps = React.useCallback(function (inputProps, otherProps) {
3178
3183
  var _extends5;
3179
3184
  var _ref6 = inputProps != null ? inputProps : {},
3180
3185
  ariaLabel = _ref6['aria-label'],
3181
- disabled = _ref6.disabled,
3182
3186
  onKeyDown = _ref6.onKeyDown,
3183
3187
  onChange = _ref6.onChange,
3184
3188
  onInput = _ref6.onInput,
@@ -3193,7 +3197,6 @@ function useCombobox(userProps) {
3193
3197
  _ref7$suppressRefErro = _ref7.suppressRefError,
3194
3198
  suppressRefError = _ref7$suppressRefErro === void 0 ? false : _ref7$suppressRefErro;
3195
3199
  setGetterPropCallInfo('getInputProps', suppressRefError, refKey, inputRef);
3196
- var latestState = latest.current.state;
3197
3200
  var inputHandleKeyDown = function inputHandleKeyDown(event) {
3198
3201
  var key = normalizeArrowKey(event);
3199
3202
  if (key && key in inputKeyDownHandlers) {
@@ -3209,7 +3212,7 @@ function useCombobox(userProps) {
3209
3212
  };
3210
3213
  var inputHandleBlur = function inputHandleBlur(event) {
3211
3214
  /* istanbul ignore else */
3212
- if (environment != null && environment.document && latestState.isOpen && !mouseAndTouchTrackers.isMouseDown) {
3215
+ if (environment != null && environment.document && stateRef.current.isOpen && !mouseAndTouchTrackers.current.isMouseDown) {
3213
3216
  var isBlurByTabChange = event.relatedTarget === null && environment.document.activeElement !== environment.document.body;
3214
3217
  dispatch({
3215
3218
  type: InputBlur,
@@ -3226,7 +3229,7 @@ function useCombobox(userProps) {
3226
3229
  /* istanbul ignore next (preact) */
3227
3230
  var onChangeKey = 'onChange';
3228
3231
  var eventHandlers = {};
3229
- if (!disabled) {
3232
+ if (!rest.disabled) {
3230
3233
  var _eventHandlers;
3231
3234
  eventHandlers = (_eventHandlers = {}, _eventHandlers[onChangeKey] = callAllEventHandlers(onChange, onInput, inputHandleChange), _eventHandlers.onKeyDown = callAllEventHandlers(onKeyDown, inputHandleKeyDown), _eventHandlers.onBlur = callAllEventHandlers(onBlur, inputHandleBlur), _eventHandlers.onClick = callAllEventHandlers(onClick, inputHandleClick), _eventHandlers);
3232
3235
  }
@@ -3244,8 +3247,8 @@ function useCombobox(userProps) {
3244
3247
  }
3245
3248
  return _extends((_extends5 = {}, _extends5[refKey] = handleRefs(ref, function (inputNode) {
3246
3249
  inputRef.current = inputNode;
3247
- }), _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);
3248
- }, [dispatch, elementIds, environment, inputKeyDownHandlers, latest, mouseAndTouchTrackers, setGetterPropCallInfo]);
3250
+ }), _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);
3251
+ }, [dispatch, elementIds, environment, inputKeyDownHandlers, isOpen, highlightedIndex, inputValue, mouseAndTouchTrackers, setGetterPropCallInfo, stateRef]);
3249
3252
 
3250
3253
  // returns
3251
3254
  var toggleMenu = React.useCallback(function () {
@@ -3567,12 +3570,13 @@ function useMultipleSelection(userProps) {
3567
3570
  // Refs.
3568
3571
  var isInitialMount = useIsInitialMount();
3569
3572
  var dropdownRef = React.useRef(null);
3570
- var selectedItemRefs = React.useRef();
3571
- selectedItemRefs.current = [];
3572
- var latest = useLatestRef({
3573
- state: state,
3574
- props: props
3575
- });
3573
+ // Map of selected-item index -> DOM node. Populated by the ref callback in
3574
+ // getSelectedItemProps and read by the focus effect. Keyed by
3575
+ // index so we never reset it during render.
3576
+ var selectedItemRefs = React.useRef(null);
3577
+ if (selectedItemRefs.current === null) {
3578
+ selectedItemRefs.current = new Map();
3579
+ }
3576
3580
 
3577
3581
  // Effects.
3578
3582
  // Adds an a11y aria live status message if getA11yStatusMessage is passed.
@@ -3584,8 +3588,9 @@ function useMultipleSelection(userProps) {
3584
3588
  }
3585
3589
  if (activeIndex === -1 && dropdownRef.current) {
3586
3590
  dropdownRef.current.focus();
3587
- } else if (selectedItemRefs.current[activeIndex]) {
3588
- selectedItemRefs.current[activeIndex].focus();
3591
+ } else {
3592
+ var _selectedItemRefs$cur;
3593
+ (_selectedItemRefs$cur = selectedItemRefs.current.get(activeIndex)) == null || _selectedItemRefs$cur.focus();
3589
3594
  }
3590
3595
  // eslint-disable-next-line react-hooks/exhaustive-deps
3591
3596
  }, [activeIndex]);
@@ -3645,10 +3650,9 @@ function useMultipleSelection(userProps) {
3645
3650
  selectedItemProp = _ref3.selectedItem,
3646
3651
  indexProp = _ref3.index,
3647
3652
  rest = _objectWithoutPropertiesLoose(_ref3, _excluded$1);
3648
- var latestState = latest.current.state;
3649
- var _getItemAndIndex = getItemAndIndex(selectedItemProp, indexProp, latestState.selectedItems, 'Pass either item or index to getSelectedItemProps!'),
3653
+ var _getItemAndIndex = getItemAndIndex(selectedItemProp, indexProp, selectedItems, 'Pass either item or index to getSelectedItemProps!'),
3650
3654
  index = _getItemAndIndex[1];
3651
- var isFocusable = index > -1 && index === latestState.activeIndex;
3655
+ var isFocusable = index > -1 && index === activeIndex;
3652
3656
  var selectedItemHandleClick = function selectedItemHandleClick() {
3653
3657
  dispatch({
3654
3658
  type: SelectedItemClick,
@@ -3663,10 +3667,12 @@ function useMultipleSelection(userProps) {
3663
3667
  };
3664
3668
  return _extends((_extends2 = {}, _extends2[refKey] = handleRefs(ref, function (selectedItemNode) {
3665
3669
  if (selectedItemNode) {
3666
- selectedItemRefs.current.push(selectedItemNode);
3670
+ selectedItemRefs.current.set(index, selectedItemNode);
3671
+ } else {
3672
+ selectedItemRefs.current["delete"](index);
3667
3673
  }
3668
3674
  }), _extends2.tabIndex = isFocusable ? 0 : -1, _extends2.onClick = callAllEventHandlers(onClick, selectedItemHandleClick), _extends2.onKeyDown = callAllEventHandlers(onKeyDown, selectedItemHandleKeyDown), _extends2), rest);
3669
- }, [dispatch, latest, selectedItemKeyDownHandlers]);
3675
+ }, [dispatch, selectedItems, activeIndex, selectedItemKeyDownHandlers]);
3670
3676
  var getDropdownProps = React.useCallback(function (_temp2, _temp3) {
3671
3677
  var _extends3;
3672
3678
  var _ref4 = _temp2 === void 0 ? {} : _temp2,
@@ -3854,8 +3860,10 @@ function useElementIdsLegacy(_ref2) {
3854
3860
  var id = _ref2.id,
3855
3861
  getTagId = _ref2.getTagId,
3856
3862
  tagGroupId = _ref2.tagGroupId;
3857
- var baseIdRef = React__namespace.useRef(id != null ? id : "downshift-" + generateId());
3858
- var baseId = baseIdRef.current;
3863
+ var _React$useState = React__namespace.useState(function () {
3864
+ return id != null ? id : "downshift-" + generateId();
3865
+ }),
3866
+ baseId = _React$useState[0];
3859
3867
  var elementIds = React__namespace.useMemo(function () {
3860
3868
  return {
3861
3869
  tagGroupId: tagGroupId != null ? tagGroupId : baseId + "-tag-group",
@@ -3952,10 +3960,6 @@ var _useTagGroup = function useTagGroup(userProps) {
3952
3960
 
3953
3961
  /* Refs */
3954
3962
 
3955
- var latest = useLatestRef({
3956
- state: state,
3957
- props: props
3958
- });
3959
3963
  var elementIds = useElementIds({
3960
3964
  getTagId: props.getTagId,
3961
3965
  id: props.id,
@@ -4018,7 +4022,6 @@ var _useTagGroup = function useTagGroup(userProps) {
4018
4022
  if (!Number.isInteger(index) || index < 0) {
4019
4023
  throw new Error('Pass correct item index to getTagProps!');
4020
4024
  }
4021
- var latestState = latest.current.state;
4022
4025
  var handleClick = function handleClick() {
4023
4026
  dispatch({
4024
4027
  type: TagClick,
@@ -4032,8 +4035,8 @@ var _useTagGroup = function useTagGroup(userProps) {
4032
4035
  if (itemNode) {
4033
4036
  itemRefs.current[tagId] = itemNode;
4034
4037
  }
4035
- }), _extends2['aria-labelledby'] = tagId, _extends2.role = 'option', _extends2.id = tagId, _extends2.onClick = callAllEventHandlers(onClick, handleClick), _extends2.tabIndex = latestState.activeIndex === index ? 0 : -1, _extends2), rest);
4036
- }, [dispatch, elementIds, latest, itemRefs]);
4038
+ }), _extends2['aria-labelledby'] = tagId, _extends2.role = 'option', _extends2.id = tagId, _extends2.onClick = callAllEventHandlers(onClick, handleClick), _extends2.tabIndex = activeIndex === index ? 0 : -1, _extends2), rest);
4039
+ }, [dispatch, elementIds, activeIndex, itemRefs]);
4037
4040
  var getTagRemoveProps = React.useCallback(function (options) {
4038
4041
  var index = options.index,
4039
4042
  onClick = options.onClick,