Files
30-seconds-of-code/node_modules/react-redux/lib/hooks/useSelector.js
2019-08-20 15:52:05 +02:00

128 lines
4.3 KiB
JavaScript

"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
exports.__esModule = true;
exports.useSelector = useSelector;
var _react = require("react");
var _invariant = _interopRequireDefault(require("invariant"));
var _useReduxContext2 = require("./useReduxContext");
var _Subscription = _interopRequireDefault(require("../utils/Subscription"));
// React currently throws a warning when using useLayoutEffect on the server.
// To get around it, we can conditionally useEffect on the server (no-op) and
// useLayoutEffect in the browser. We need useLayoutEffect to ensure the store
// subscription callback always has the selector from the latest render commit
// available, otherwise a store update may happen between render and the effect,
// which may cause missed updates; we also must ensure the store subscription
// is created synchronously, otherwise a store update may occur before the
// subscription is created and an inconsistent state may be observed
var useIsomorphicLayoutEffect = typeof window !== 'undefined' ? _react.useLayoutEffect : _react.useEffect;
var refEquality = function refEquality(a, b) {
return a === b;
};
/**
* A hook to access the redux store's state. This hook takes a selector function
* as an argument. The selector is called with the store state.
*
* This hook takes an optional equality comparison function as the second parameter
* that allows you to customize the way the selected state is compared to determine
* whether the component needs to be re-rendered.
*
* @param {Function} selector the selector function
* @param {Function=} equalityFn the function that will be used to determine equality
*
* @returns {any} the selected state
*
* @example
*
* import React from 'react'
* import { useSelector } from 'react-redux'
*
* export const CounterComponent = () => {
* const counter = useSelector(state => state.counter)
* return <div>{counter}</div>
* }
*/
function useSelector(selector, equalityFn) {
if (equalityFn === void 0) {
equalityFn = refEquality;
}
(0, _invariant["default"])(selector, "You must pass a selector to useSelectors");
var _useReduxContext = (0, _useReduxContext2.useReduxContext)(),
store = _useReduxContext.store,
contextSub = _useReduxContext.subscription;
var _useReducer = (0, _react.useReducer)(function (s) {
return s + 1;
}, 0),
forceRender = _useReducer[1];
var subscription = (0, _react.useMemo)(function () {
return new _Subscription["default"](store, contextSub);
}, [store, contextSub]);
var latestSubscriptionCallbackError = (0, _react.useRef)();
var latestSelector = (0, _react.useRef)();
var latestSelectedState = (0, _react.useRef)();
var selectedState;
try {
if (selector !== latestSelector.current || latestSubscriptionCallbackError.current) {
selectedState = selector(store.getState());
} else {
selectedState = latestSelectedState.current;
}
} catch (err) {
var errorMessage = "An error occured while selecting the store state: " + err.message + ".";
if (latestSubscriptionCallbackError.current) {
errorMessage += "\nThe error may be correlated with this previous error:\n" + latestSubscriptionCallbackError.current.stack + "\n\nOriginal stack trace:";
}
throw new Error(errorMessage);
}
useIsomorphicLayoutEffect(function () {
latestSelector.current = selector;
latestSelectedState.current = selectedState;
latestSubscriptionCallbackError.current = undefined;
});
useIsomorphicLayoutEffect(function () {
function checkForUpdates() {
try {
var newSelectedState = latestSelector.current(store.getState());
if (equalityFn(newSelectedState, latestSelectedState.current)) {
return;
}
latestSelectedState.current = newSelectedState;
} catch (err) {
// we ignore all errors here, since when the component
// is re-rendered, the selectors are called again, and
// will throw again, if neither props nor store state
// changed
latestSubscriptionCallbackError.current = err;
}
forceRender({});
}
subscription.onStateChange = checkForUpdates;
subscription.trySubscribe();
checkForUpdates();
return function () {
return subscription.tryUnsubscribe();
};
}, [store, subscription]);
return selectedState;
}