@anew/hooks
Version:
AnewJS React Hooks
266 lines (218 loc) • 7.06 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react')) :
typeof define === 'function' && define.amd ? define(['exports', 'react'], factory) :
(global = global || self, factory(global.Redux = {}, global.react));
}(this, (function (exports, react) { 'use strict';
function is(x, y) {
if (x === y) {
return x !== 0 || y !== 0 || 1 / x === 1 / y;
} else {
return x !== x && y !== y;
}
}
function shallowEqual(objA, objB) {
if (is(objA, objB)) return true;
if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
return false;
}
var keysA = Object.keys(objA);
var keysB = Object.keys(objB);
if (keysA.length !== keysB.length) return false;
for (var i = 0; i < keysA.length; i++) {
if (!Object.prototype.hasOwnProperty.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) {
return false;
}
}
return true;
}
// Default to a dummy "batch" implementation that just runs the callback
var defaultNoopBatch = function defaultNoopBatch(callback) {
callback();
};
var batch = defaultNoopBatch; // Allow injecting another batching function later
var getBatch = function getBatch() {
return batch;
};
var nullListeners = {
notify: function notify() {}
};
function createListenerCollection() {
var batch = getBatch();
var first = null;
var last = null;
return {
clear: function clear() {
first = null;
last = null;
},
notify: function notify() {
batch(function () {
var listener = first;
while (listener) {
listener.callback();
listener = listener.next;
}
});
},
get: function get() {
var listeners = [];
var listener = first;
while (listener) {
listeners.push(listener);
listener = listener.next;
}
return listeners;
},
subscribe: function subscribe(callback) {
var isSubscribed = true;
var listener = last = {
callback: callback,
next: null,
prev: last
};
if (listener.prev) {
listener.prev.next = listener;
} else {
first = listener;
}
return function unsubscribe() {
if (!isSubscribed || first === null) return;
isSubscribed = false;
if (listener.next) {
listener.next.prev = listener.prev;
} else {
last = listener.prev;
}
if (listener.prev) {
listener.prev.next = listener.next;
} else {
first = listener.next;
}
};
}
};
}
var Subscription = /*#__PURE__*/function () {
function Subscription(store) {
var _this = this;
this.store = store;
this.unsubscribe = null;
this.listeners = nullListeners;
this.handleChangeWrapper = function () {
if (_this.onStateChange) {
_this.onStateChange();
}
};
}
var _proto = Subscription.prototype;
_proto.notifyNestedSubs = function notifyNestedSubs() {
this.listeners.notify();
};
_proto.isSubscribed = function isSubscribed() {
return Boolean(this.unsubscribe);
};
_proto.trySubscribe = function trySubscribe() {
if (!this.unsubscribe) {
this.unsubscribe = this.store.subscribe(this.handleChangeWrapper);
this.listeners = createListenerCollection();
}
};
_proto.tryUnsubscribe = function tryUnsubscribe() {
if (this.unsubscribe) {
this.unsubscribe();
this.unsubscribe = null;
if (this.listeners && this.listeners.clear) this.listeners.clear();
this.listeners = nullListeners;
}
};
return Subscription;
}();
var refEquality = function refEquality(a, b) {
return a === b;
};
var useIsomorphicLayoutEffect = typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined' ? react.useLayoutEffect : react.useEffect;
function useStoreStateWithStoreAndSubscription(store, selector, equalityFn) {
var _useReducer = react.useReducer(function (s) {
return s + 1;
}, 0),
forceRender = _useReducer[1];
var subscription = react.useMemo(function () {
return new Subscription(store);
}, [store]);
var latestSubscriptionCallbackError = react.useRef();
var latestSelector = react.useRef();
var latestStoreState = react.useRef();
var latestSelectedState = react.useRef();
var storeState = store.state;
var selectedState;
try {
if (selector !== latestSelector.current || storeState !== latestStoreState.current || latestSubscriptionCallbackError.current) {
selectedState = selector(storeState);
} else {
selectedState = latestSelectedState.current;
}
} catch (err) {
if (latestSubscriptionCallbackError.current) {
err.message += "\nThe error may be correlated with this previous error:\n" + latestSubscriptionCallbackError.current.stack + "\n\n";
}
throw err;
}
useIsomorphicLayoutEffect(function () {
latestSelector.current = selector;
latestStoreState.current = storeState;
latestSelectedState.current = selectedState;
latestSubscriptionCallbackError.current = undefined;
});
useIsomorphicLayoutEffect(function () {
function checkForUpdates() {
try {
var newSelectedState = latestSelector.current(store.state);
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;
}
/**
* Hook factory, which creates a `useStoreState` hook bound to a given store.
*
* @param {Store} [store] Store
* @returns {Function} A `useStoreState` hook bound to the specified store.
*/
function createUseStoreState(store) {
function useStoreState(selector, equalityFn) {
if (equalityFn === void 0) {
equalityFn = refEquality;
}
if ( !selector) {
throw new Error("You must pass a selector to useStoreState");
}
var selectedState = useStoreStateWithStoreAndSubscription(store, selector, equalityFn);
react.useDebugValue(selectedState);
return selectedState;
}
useStoreState.withSallowEqual = function useStoreStateWithShallowEqual(selector) {
return useStoreState(selector, shallowEqual);
};
return useStoreState;
}
exports.createUseStoreState = createUseStoreState;
exports.shallowEqual = shallowEqual;
Object.defineProperty(exports, '__esModule', { value: true });
})));