useSelector.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. "use strict";
  2. var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
  3. exports.__esModule = true;
  4. exports.createSelectorHook = createSelectorHook;
  5. exports.useSelector = void 0;
  6. var _react = require("react");
  7. var _useReduxContext2 = require("./useReduxContext");
  8. var _Subscription = _interopRequireDefault(require("../utils/Subscription"));
  9. var _useIsomorphicLayoutEffect = require("../utils/useIsomorphicLayoutEffect");
  10. var _Context = require("../components/Context");
  11. var refEquality = function refEquality(a, b) {
  12. return a === b;
  13. };
  14. function useSelectorWithStoreAndSubscription(selector, equalityFn, store, contextSub) {
  15. var _useReducer = (0, _react.useReducer)(function (s) {
  16. return s + 1;
  17. }, 0),
  18. forceRender = _useReducer[1];
  19. var subscription = (0, _react.useMemo)(function () {
  20. return new _Subscription["default"](store, contextSub);
  21. }, [store, contextSub]);
  22. var latestSubscriptionCallbackError = (0, _react.useRef)();
  23. var latestSelector = (0, _react.useRef)();
  24. var latestSelectedState = (0, _react.useRef)();
  25. var selectedState;
  26. try {
  27. if (selector !== latestSelector.current || latestSubscriptionCallbackError.current) {
  28. selectedState = selector(store.getState());
  29. } else {
  30. selectedState = latestSelectedState.current;
  31. }
  32. } catch (err) {
  33. if (latestSubscriptionCallbackError.current) {
  34. err.message += "\nThe error may be correlated with this previous error:\n" + latestSubscriptionCallbackError.current.stack + "\n\n";
  35. }
  36. throw err;
  37. }
  38. (0, _useIsomorphicLayoutEffect.useIsomorphicLayoutEffect)(function () {
  39. latestSelector.current = selector;
  40. latestSelectedState.current = selectedState;
  41. latestSubscriptionCallbackError.current = undefined;
  42. });
  43. (0, _useIsomorphicLayoutEffect.useIsomorphicLayoutEffect)(function () {
  44. function checkForUpdates() {
  45. try {
  46. var newSelectedState = latestSelector.current(store.getState());
  47. if (equalityFn(newSelectedState, latestSelectedState.current)) {
  48. return;
  49. }
  50. latestSelectedState.current = newSelectedState;
  51. } catch (err) {
  52. // we ignore all errors here, since when the component
  53. // is re-rendered, the selectors are called again, and
  54. // will throw again, if neither props nor store state
  55. // changed
  56. latestSubscriptionCallbackError.current = err;
  57. }
  58. forceRender({});
  59. }
  60. subscription.onStateChange = checkForUpdates;
  61. subscription.trySubscribe();
  62. checkForUpdates();
  63. return function () {
  64. return subscription.tryUnsubscribe();
  65. };
  66. }, [store, subscription]);
  67. return selectedState;
  68. }
  69. /**
  70. * Hook factory, which creates a `useSelector` hook bound to a given context.
  71. *
  72. * @param {React.Context} [context=ReactReduxContext] Context passed to your `<Provider>`.
  73. * @returns {Function} A `useSelector` hook bound to the specified context.
  74. */
  75. function createSelectorHook(context) {
  76. if (context === void 0) {
  77. context = _Context.ReactReduxContext;
  78. }
  79. var useReduxContext = context === _Context.ReactReduxContext ? _useReduxContext2.useReduxContext : function () {
  80. return (0, _react.useContext)(context);
  81. };
  82. return function useSelector(selector, equalityFn) {
  83. if (equalityFn === void 0) {
  84. equalityFn = refEquality;
  85. }
  86. if (process.env.NODE_ENV !== 'production' && !selector) {
  87. throw new Error("You must pass a selector to useSelectors");
  88. }
  89. var _useReduxContext = useReduxContext(),
  90. store = _useReduxContext.store,
  91. contextSub = _useReduxContext.subscription;
  92. return useSelectorWithStoreAndSubscription(selector, equalityFn, store, contextSub);
  93. };
  94. }
  95. /**
  96. * A hook to access the redux store's state. This hook takes a selector function
  97. * as an argument. The selector is called with the store state.
  98. *
  99. * This hook takes an optional equality comparison function as the second parameter
  100. * that allows you to customize the way the selected state is compared to determine
  101. * whether the component needs to be re-rendered.
  102. *
  103. * @param {Function} selector the selector function
  104. * @param {Function=} equalityFn the function that will be used to determine equality
  105. *
  106. * @returns {any} the selected state
  107. *
  108. * @example
  109. *
  110. * import React from 'react'
  111. * import { useSelector } from 'react-redux'
  112. *
  113. * export const CounterComponent = () => {
  114. * const counter = useSelector(state => state.counter)
  115. * return <div>{counter}</div>
  116. * }
  117. */
  118. var useSelector =
  119. /*#__PURE__*/
  120. createSelectorHook();
  121. exports.useSelector = useSelector;