useSelector.js 5.5 KB

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