useSelector.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. "use strict";
  2. exports.__esModule = true;
  3. exports.createSelectorHook = createSelectorHook;
  4. exports.useSelector = exports.initializeUseSelector = void 0;
  5. var _react = require("react");
  6. var _useReduxContext = require("./useReduxContext");
  7. var _Context = require("../components/Context");
  8. var _useSyncExternalStore = require("../utils/useSyncExternalStore");
  9. let useSyncExternalStoreWithSelector = _useSyncExternalStore.notInitialized;
  10. const initializeUseSelector = fn => {
  11. useSyncExternalStoreWithSelector = fn;
  12. };
  13. exports.initializeUseSelector = initializeUseSelector;
  14. const refEquality = (a, b) => a === b;
  15. /**
  16. * Hook factory, which creates a `useSelector` hook bound to a given context.
  17. *
  18. * @param {React.Context} [context=ReactReduxContext] Context passed to your `<Provider>`.
  19. * @returns {Function} A `useSelector` hook bound to the specified context.
  20. */
  21. function createSelectorHook(context = _Context.ReactReduxContext) {
  22. const useReduxContext = context === _Context.ReactReduxContext ? _useReduxContext.useReduxContext : (0, _useReduxContext.createReduxContextHook)(context);
  23. return function useSelector(selector, equalityFnOrOptions = {}) {
  24. const {
  25. equalityFn = refEquality,
  26. stabilityCheck = undefined,
  27. noopCheck = undefined
  28. } = typeof equalityFnOrOptions === 'function' ? {
  29. equalityFn: equalityFnOrOptions
  30. } : equalityFnOrOptions;
  31. if (process.env.NODE_ENV !== 'production') {
  32. if (!selector) {
  33. throw new Error(`You must pass a selector to useSelector`);
  34. }
  35. if (typeof selector !== 'function') {
  36. throw new Error(`You must pass a function as a selector to useSelector`);
  37. }
  38. if (typeof equalityFn !== 'function') {
  39. throw new Error(`You must pass a function as an equality function to useSelector`);
  40. }
  41. }
  42. const {
  43. store,
  44. subscription,
  45. getServerState,
  46. stabilityCheck: globalStabilityCheck,
  47. noopCheck: globalNoopCheck
  48. } = useReduxContext();
  49. const firstRun = (0, _react.useRef)(true);
  50. const wrappedSelector = (0, _react.useCallback)({
  51. [selector.name](state) {
  52. const selected = selector(state);
  53. if (process.env.NODE_ENV !== 'production') {
  54. const finalStabilityCheck = typeof stabilityCheck === 'undefined' ? globalStabilityCheck : stabilityCheck;
  55. if (finalStabilityCheck === 'always' || finalStabilityCheck === 'once' && firstRun.current) {
  56. const toCompare = selector(state);
  57. if (!equalityFn(selected, toCompare)) {
  58. let stack = undefined;
  59. try {
  60. throw new Error();
  61. } catch (e) {
  62. ;
  63. ({
  64. stack
  65. } = e);
  66. }
  67. console.warn('Selector ' + (selector.name || 'unknown') + ' returned a different result when called with the same parameters. This can lead to unnecessary rerenders.' + '\nSelectors that return a new reference (such as an object or an array) should be memoized: https://redux.js.org/usage/deriving-data-selectors#optimizing-selectors-with-memoization', {
  68. state,
  69. selected,
  70. selected2: toCompare,
  71. stack
  72. });
  73. }
  74. }
  75. const finalNoopCheck = typeof noopCheck === 'undefined' ? globalNoopCheck : noopCheck;
  76. if (finalNoopCheck === 'always' || finalNoopCheck === 'once' && firstRun.current) {
  77. // @ts-ignore
  78. if (selected === state) {
  79. let stack = undefined;
  80. try {
  81. throw new Error();
  82. } catch (e) {
  83. ;
  84. ({
  85. stack
  86. } = e);
  87. }
  88. console.warn('Selector ' + (selector.name || 'unknown') + ' returned the root state when called. This can lead to unnecessary rerenders.' + '\nSelectors that return the entire state are almost certainly a mistake, as they will cause a rerender whenever *anything* in state changes.', {
  89. stack
  90. });
  91. }
  92. }
  93. if (firstRun.current) firstRun.current = false;
  94. }
  95. return selected;
  96. }
  97. }[selector.name], [selector, globalStabilityCheck, stabilityCheck]);
  98. const selectedState = useSyncExternalStoreWithSelector(subscription.addNestedSub, store.getState, getServerState || store.getState, wrappedSelector, equalityFn);
  99. (0, _react.useDebugValue)(selectedState);
  100. return selectedState;
  101. };
  102. }
  103. /**
  104. * A hook to access the redux store's state. This hook takes a selector function
  105. * as an argument. The selector is called with the store state.
  106. *
  107. * This hook takes an optional equality comparison function as the second parameter
  108. * that allows you to customize the way the selected state is compared to determine
  109. * whether the component needs to be re-rendered.
  110. *
  111. * @param {Function} selector the selector function
  112. * @param {Function=} equalityFn the function that will be used to determine equality
  113. *
  114. * @returns {any} the selected state
  115. *
  116. * @example
  117. *
  118. * import React from 'react'
  119. * import { useSelector } from 'react-redux'
  120. *
  121. * export const CounterComponent = () => {
  122. * const counter = useSelector(state => state.counter)
  123. * return <div>{counter}</div>
  124. * }
  125. */
  126. const useSelector = /*#__PURE__*/createSelectorHook();
  127. exports.useSelector = useSelector;