react-async-call
Version:
Declarative promise handling in React
1,285 lines (1,078 loc) • 46.9 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var React = require('react');
var React__default = _interopDefault(React);
var PropTypes = require('prop-types');
var PropTypes__default = _interopDefault(PropTypes);
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
function makeEmptyFunction(arg) {
return function () {
return arg;
};
}
/**
* This function accepts and discards inputs; it has no side effects. This is
* primarily useful idiomatically for overridable function endpoints which
* always need to be callable, since JS lacks a null-call idiom ala Cocoa.
*/
function emptyFunction() {}
emptyFunction.thatReturns = makeEmptyFunction;
emptyFunction.thatReturnsFalse = makeEmptyFunction(false);
emptyFunction.thatReturnsTrue = makeEmptyFunction(true);
emptyFunction.thatReturnsNull = makeEmptyFunction(null);
emptyFunction.thatReturnsThis = function () {
return this;
};
emptyFunction.thatReturnsArgument = function (arg) {
return arg;
};
var emptyFunction_1 = emptyFunction;
/**
* Similar to invariant but only logs a warning if the condition is not met.
* This can be used to log issues in development environments in critical
* paths. Removing the logging code for production environments will keep the
* same logic and follow the same code paths.
*/
var warning = emptyFunction_1;
var warning_1 = warning;
/**
* Copyright (c) 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @typechecks
*
*/
var hasOwnProperty = Object.prototype.hasOwnProperty;
/**
* inlined Object.is polyfill to avoid requiring consumers ship their own
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
*/
function is(x, y) {
// SameValue algorithm
if (x === y) {
// Steps 1-5, 7-10
// Steps 6.b-6.e: +0 != -0
return x !== 0 || 1 / x === 1 / y;
} else {
// Step 6.a: NaN == NaN
return x !== x && y !== y;
}
}
/**
* Performs equality by iterating through keys on an object and returning false
* when any key has values which are not strictly equal between the arguments.
* Returns true when the values of all keys are strictly equal.
*/
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;
}
// Test for A's keys different from B.
for (var i = 0; i < keysA.length; i++) {
if (!hasOwnProperty.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) {
return false;
}
}
return true;
}
var shallowEqual_1 = shallowEqual;
var isFunction = function isFunction(value) {
return !!(value && value.constructor && value.call && value.apply);
};
var renderChildren = function renderChildren(props, arg) {
return (isFunction(props.children) ? props.children(arg) : props.children) || null;
};
var renderChildrenFn = function renderChildrenFn(props, arg) {
return isFunction(props.children) && props.children(arg) || null;
};
var createAsyncCallChild = function createAsyncCallChild(Consumer, rootDisplayName, renderFn, displayName, childrenPropType) {
var Child = function Child(props) {
return React.createElement(
Consumer,
null,
function (contextProps) {
invariant(contextProps, INVARIANT_MUST_BE_A_CHILD, Child.displayName, rootDisplayName);
return renderFn(props, contextProps) || null;
}
);
};
return Child;
};
var nodePropType = void 0;
var requiredFuncPropType = void 0;
var nodeOrFuncPropType = void 0;
var INVARIANT_MUST_BE_A_CHILD = void 0;
var INVARIANT_FUNCTION_SHOULD_BE_PASSED = void 0;
var INVARIANT_FUNCTION_SHOULD_RETURN_PROMISE = void 0;
var WARNING_PROPERTY_RESET_IS_DEPRECATED = void 0;
var DISPLAY_NAME_COMPLETED = void 0;
var DISPLAY_NAME_EXECUTOR = void 0;
var DISPLAY_NAME_REJECTED = void 0;
var DISPLAY_NAME_RESOLVED = void 0;
var DISPLAY_NAME_RUNNING = void 0;
var DISPLAY_NAME_STATE = void 0;
var DISPLAY_NAME_RESETTER = void 0;
var DISPLAY_NAME_HAS_RESULT = void 0;
/**
* Use invariant() to assert state which your program assumes to be true.
*
* Provide sprintf-style format (only %s is supported) and arguments
* to provide information about what broke and what you were
* expecting.
*
* The invariant message will be stripped in production, but the invariant
* will remain to ensure logic does not differ in production.
*/
function invariant(condition, format, a, b, c, d, e, f) {
if (!condition) {
var error;
{
error = new Error('Minified exception occurred; use the non-minified dev environment ' + 'for the full error message and additional helpful warnings.');
}
error.framesToPop = 1; // we don't care about invariant's own frame
throw error;
}
}
var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
function unwrapExports (x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
}
function createCommonjsModule(fn, module) {
return module = { exports: {} }, fn(module, module.exports), module.exports;
}
var key = '__global_unique_id__';
var gud = function() {
return commonjsGlobal[key] = (commonjsGlobal[key] || 0) + 1;
};
var implementation = createCommonjsModule(function (module, exports) {
exports.__esModule = true;
var _react2 = _interopRequireDefault(React__default);
var _propTypes2 = _interopRequireDefault(PropTypes__default);
var _gud2 = _interopRequireDefault(gud);
var _warning2 = _interopRequireDefault(warning_1);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var MAX_SIGNED_31_BIT_INT = 1073741823;
// Inlined Object.is polyfill.
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
function objectIs(x, y) {
if (x === y) {
return x !== 0 || 1 / x === 1 / y;
} else {
return x !== x && y !== y;
}
}
function createEventEmitter(value) {
var handlers = [];
return {
on: function on(handler) {
handlers.push(handler);
},
off: function off(handler) {
handlers = handlers.filter(function (h) {
return h !== handler;
});
},
get: function get() {
return value;
},
set: function set(newValue, changedBits) {
value = newValue;
handlers.forEach(function (handler) {
return handler(value, changedBits);
});
}
};
}
function onlyChild(children) {
return Array.isArray(children) ? children[0] : children;
}
function createReactContext(defaultValue, calculateChangedBits) {
var _Provider$childContex, _Consumer$contextType;
var contextProp = '__create-react-context-' + (0, _gud2.default)() + '__';
var Provider = function (_Component) {
_inherits(Provider, _Component);
function Provider() {
var _temp, _this, _ret;
_classCallCheck(this, Provider);
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return _ret = (_temp = (_this = _possibleConstructorReturn(this, _Component.call.apply(_Component, [this].concat(args))), _this), _this.emitter = createEventEmitter(_this.props.value), _temp), _possibleConstructorReturn(_this, _ret);
}
Provider.prototype.getChildContext = function getChildContext() {
var _ref;
return _ref = {}, _ref[contextProp] = this.emitter, _ref;
};
Provider.prototype.componentWillReceiveProps = function componentWillReceiveProps(nextProps) {
if (this.props.value !== nextProps.value) {
var oldValue = this.props.value;
var newValue = nextProps.value;
var changedBits = void 0;
if (objectIs(oldValue, newValue)) {
changedBits = 0; // No change
} else {
changedBits = typeof calculateChangedBits === 'function' ? calculateChangedBits(oldValue, newValue) : MAX_SIGNED_31_BIT_INT;
changedBits |= 0;
if (changedBits !== 0) {
this.emitter.set(nextProps.value, changedBits);
}
}
}
};
Provider.prototype.render = function render() {
return this.props.children;
};
return Provider;
}(React__default.Component);
Provider.childContextTypes = (_Provider$childContex = {}, _Provider$childContex[contextProp] = _propTypes2.default.object.isRequired, _Provider$childContex);
var Consumer = function (_Component2) {
_inherits(Consumer, _Component2);
function Consumer() {
var _temp2, _this2, _ret2;
_classCallCheck(this, Consumer);
for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = arguments[_key2];
}
return _ret2 = (_temp2 = (_this2 = _possibleConstructorReturn(this, _Component2.call.apply(_Component2, [this].concat(args))), _this2), _this2.state = {
value: _this2.getValue()
}, _this2.onUpdate = function (newValue, changedBits) {
var observedBits = _this2.observedBits | 0;
if ((observedBits & changedBits) !== 0) {
_this2.setState({ value: _this2.getValue() });
}
}, _temp2), _possibleConstructorReturn(_this2, _ret2);
}
Consumer.prototype.componentWillReceiveProps = function componentWillReceiveProps(nextProps) {
var observedBits = nextProps.observedBits;
this.observedBits = observedBits === undefined || observedBits === null ? MAX_SIGNED_31_BIT_INT // Subscribe to all changes by default
: observedBits;
};
Consumer.prototype.componentDidMount = function componentDidMount() {
if (this.context[contextProp]) {
this.context[contextProp].on(this.onUpdate);
}
var observedBits = this.props.observedBits;
this.observedBits = observedBits === undefined || observedBits === null ? MAX_SIGNED_31_BIT_INT // Subscribe to all changes by default
: observedBits;
};
Consumer.prototype.componentWillUnmount = function componentWillUnmount() {
if (this.context[contextProp]) {
this.context[contextProp].off(this.onUpdate);
}
};
Consumer.prototype.getValue = function getValue() {
if (this.context[contextProp]) {
return this.context[contextProp].get();
} else {
return defaultValue;
}
};
Consumer.prototype.render = function render() {
return onlyChild(this.props.children)(this.state.value);
};
return Consumer;
}(React__default.Component);
Consumer.contextTypes = (_Consumer$contextType = {}, _Consumer$contextType[contextProp] = _propTypes2.default.object, _Consumer$contextType);
return {
Provider: Provider,
Consumer: Consumer
};
}
exports.default = createReactContext;
module.exports = exports['default'];
});
unwrapExports(implementation);
var lib = createCommonjsModule(function (module, exports) {
exports.__esModule = true;
var _react2 = _interopRequireDefault(React__default);
var _implementation2 = _interopRequireDefault(implementation);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
exports.default = _react2.default.createContext || _implementation2.default;
module.exports = exports['default'];
});
var createContext = unwrapExports(lib);
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
function componentWillMount() {
// Call this.constructor.gDSFP to support sub-classes.
var state = this.constructor.getDerivedStateFromProps(this.props, this.state);
if (state !== null && state !== undefined) {
this.setState(state);
}
}
function componentWillReceiveProps(nextProps) {
// Call this.constructor.gDSFP to support sub-classes.
// Use the setState() updater to ensure state isn't stale in certain edge cases.
function updater(prevState) {
var state = this.constructor.getDerivedStateFromProps(nextProps, prevState);
return state !== null && state !== undefined ? state : null;
}
// Binding "this" is important for shallow renderer support.
this.setState(updater.bind(this));
}
function componentWillUpdate(nextProps, nextState) {
try {
var prevProps = this.props;
var prevState = this.state;
this.props = nextProps;
this.state = nextState;
this.__reactInternalSnapshotFlag = true;
this.__reactInternalSnapshot = this.getSnapshotBeforeUpdate(
prevProps,
prevState
);
} finally {
this.props = prevProps;
this.state = prevState;
}
}
// React may warn about cWM/cWRP/cWU methods being deprecated.
// Add a flag to suppress these warnings for this special case.
componentWillMount.__suppressDeprecationWarning = true;
componentWillReceiveProps.__suppressDeprecationWarning = true;
componentWillUpdate.__suppressDeprecationWarning = true;
function polyfill(Component) {
var prototype = Component.prototype;
if (!prototype || !prototype.isReactComponent) {
throw new Error('Can only polyfill class components');
}
if (
typeof Component.getDerivedStateFromProps !== 'function' &&
typeof prototype.getSnapshotBeforeUpdate !== 'function'
) {
return Component;
}
// If new component APIs are defined, "unsafe" lifecycles won't be called.
// Error if any of these lifecycles are present,
// Because they would work differently between older and newer (16.3+) versions of React.
var foundWillMountName = null;
var foundWillReceivePropsName = null;
var foundWillUpdateName = null;
if (typeof prototype.componentWillMount === 'function') {
foundWillMountName = 'componentWillMount';
} else if (typeof prototype.UNSAFE_componentWillMount === 'function') {
foundWillMountName = 'UNSAFE_componentWillMount';
}
if (typeof prototype.componentWillReceiveProps === 'function') {
foundWillReceivePropsName = 'componentWillReceiveProps';
} else if (typeof prototype.UNSAFE_componentWillReceiveProps === 'function') {
foundWillReceivePropsName = 'UNSAFE_componentWillReceiveProps';
}
if (typeof prototype.componentWillUpdate === 'function') {
foundWillUpdateName = 'componentWillUpdate';
} else if (typeof prototype.UNSAFE_componentWillUpdate === 'function') {
foundWillUpdateName = 'UNSAFE_componentWillUpdate';
}
if (
foundWillMountName !== null ||
foundWillReceivePropsName !== null ||
foundWillUpdateName !== null
) {
var componentName = Component.displayName || Component.name;
var newApiName =
typeof Component.getDerivedStateFromProps === 'function'
? 'getDerivedStateFromProps()'
: 'getSnapshotBeforeUpdate()';
throw Error(
'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' +
componentName +
' uses ' +
newApiName +
' but also contains the following legacy lifecycles:' +
(foundWillMountName !== null ? '\n ' + foundWillMountName : '') +
(foundWillReceivePropsName !== null
? '\n ' + foundWillReceivePropsName
: '') +
(foundWillUpdateName !== null ? '\n ' + foundWillUpdateName : '') +
'\n\nThe above lifecycles should be removed. Learn more about this warning here:\n' +
'https://fb.me/react-async-component-lifecycle-hooks'
);
}
// React <= 16.2 does not support static getDerivedStateFromProps.
// As a workaround, use cWM and cWRP to invoke the new static lifecycle.
// Newer versions of React will ignore these lifecycles if gDSFP exists.
if (typeof Component.getDerivedStateFromProps === 'function') {
prototype.componentWillMount = componentWillMount;
prototype.componentWillReceiveProps = componentWillReceiveProps;
}
// React <= 16.2 does not support getSnapshotBeforeUpdate.
// As a workaround, use cWU to invoke the new lifecycle.
// Newer versions of React will ignore that lifecycle if gSBU exists.
if (typeof prototype.getSnapshotBeforeUpdate === 'function') {
if (typeof prototype.componentDidUpdate !== 'function') {
throw new Error(
'Cannot polyfill getSnapshotBeforeUpdate() for components that do not define componentDidUpdate() on the prototype'
);
}
prototype.componentWillUpdate = componentWillUpdate;
var componentDidUpdate = prototype.componentDidUpdate;
prototype.componentDidUpdate = function componentDidUpdatePolyfill(
prevProps,
prevState,
maybeSnapshot
) {
// 16.3+ will not execute our will-update method;
// It will pass a snapshot value to did-update though.
// Older versions will require our polyfilled will-update value.
// We need to handle both cases, but can't just check for the presence of "maybeSnapshot",
// Because for <= 15.x versions this might be a "prevContext" object.
// We also can't just check "__reactInternalSnapshot",
// Because get-snapshot might return a falsy value.
// So check for the explicit __reactInternalSnapshotFlag flag to determine behavior.
var snapshot = this.__reactInternalSnapshotFlag
? this.__reactInternalSnapshot
: maybeSnapshot;
componentDidUpdate.call(this, prevProps, prevState, snapshot);
};
}
return Component;
}
var classCallCheck = function (instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
};
var _extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
var inherits = function (subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true
}
});
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
};
var possibleConstructorReturn = function (self, call) {
if (!self) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return call && (typeof call === "object" || typeof call === "function") ? call : self;
};
var INITIAL_VALUE_PROP_NAME = 'initialValue';
var createResultStore = function createResultStore(RootConsumer, rootDisplayName) {
var _createContext = createContext(),
Consumer = _createContext.Consumer,
Provider = _createContext.Provider;
var Internal = function (_React$Component) {
inherits(Internal, _React$Component);
function Internal() {
var _temp, _this, _ret;
classCallCheck(this, Internal);
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return _ret = (_temp = (_this = possibleConstructorReturn(this, _React$Component.call.apply(_React$Component, [this].concat(args))), _this), _this.state = {
hasResult: false,
wasResolved: _this.props.resolved
}, _this.reset = function (execute) {
_this.setState(getResetState(_this.props));
if (execute) {
execute();
}
}, _temp), possibleConstructorReturn(_this, _ret);
}
Internal.prototype.componentDidMount = function componentDidMount() {
var props = this.props;
var resolved = props.resolved;
if (resolved || props.hasOwnProperty(INITIAL_VALUE_PROP_NAME)) {
this.setState({
hasResult: true,
result: resolved ? props.result : props.initialValue,
wasResolved: resolved
});
}
};
Internal.getDerivedStateFromProps = function getDerivedStateFromProps(nextProps, prevState) {
var resolved = nextProps.resolved,
reset = nextProps.reset;
if (reset) {
return _extends({}, getResetState(nextProps), { wasResolved: resolved });
}
if (resolved && !prevState.wasResolved) {
if (prevState.hasResult) {
return {
result: nextProps.reduce(prevState.result, nextProps.result),
wasResolved: resolved
};
}
return {
hasResult: true,
result: nextProps.result,
wasResolved: resolved
};
}
if (resolved !== prevState.wasResolved) {
return {
wasResolved: resolved
};
}
return null;
};
Internal.prototype.render = function render() {
var state = this.state;
var props = this.props;
var value = _extends({
hasResult: state.hasResult,
reset: props.resetFn
}, state.hasResult ? { result: state.result } : {});
return React.createElement(
Provider,
{ value: value },
renderChildren(props, value)
);
};
return Internal;
}(React.Component);
var getResetState = function getResetState(props) {
return props.hasOwnProperty(INITIAL_VALUE_PROP_NAME) ? { hasResult: true, result: props.initialValue } : {
hasResult: false
};
};
var ResultStoreInternal = polyfill(Internal);
/**
* Type of `children` function of a {@link AsyncCall.ResultStore} component.
* @function ResultStoreChildrenFunction
* @param {object} params
* @param {boolean} params.hasResult
* @param {any=} params.result
* @param {ResetFunction} params.reset Function for manual store cleaning.
* @returns {ReactNode} Should return rendered React component(s) depending on supplied params.
* @remark type definition
*/
/**
* Type of `reduce` property of a {@link AsyncCall.ResultStore} component.
* @function ReduceFunction
* @param {any} previousResult
* @param {any} currentResult
* @returns {any}
* @remark type definition
*/
/**
* @class
* @classdesc
* React Component. Implements store of results of sequential async calls.
* Useful when you need to accumulate results of async calls (e.g., to glue together sequential calls of server API).
* @example
* ```jsx
* const concatResults = (previousResult, currentResult) => [...previousResult, ...currentResult]
*
* ...
*
* <AsyncCall params={{ page: this.state.page }}>
* <AsyncCall.ResultStore reduce={concatResults} reset={this.state.page === 0}>
* <AsyncCall.ResultStore.HasResult>
* {({ result }) => <pre>{JSON.stringify(result)}</pre>}
* </AsyncCall.ResultStore.HasResult>
* </AsyncCall.ResultStore>
* <button onClick={() => this.setState(({ page }) => ({ page: page + 1}))}>
* Load more
* </button>
* <button onClick={() => this.setState({ page: 0 })}>
* Reload
* </button>
* </AsyncCall>
* ```
* @property {ResultStoreChildrenFunction | ReactNode} children React children or function that returns rendered result
* depending on `hasResult` flag and `result`.
* @property {ReduceFunction} reduce Function from previousResult and currentResult to a new result.
* Useful, for example, when you need to accumulate sequential async calls
* (e.g. for fetching data for infinte page scroll).
* @property {any=} initialValue Optional initial value for the result store. If value is provided, result store will have result always.
* @property {boolean} [reset=false] @deprecated If `true`, clears the store (**Deprecated, will be removed in version 1.0.0. Use {@link AsyncCall.ResultStore.Resetter} instead**).
* @static
* @extends {React.Component}
* @memberof AsyncCall
*/
var ResultStore = function (_React$Component2) {
inherits(ResultStore, _React$Component2);
function ResultStore() {
var _temp2, _this2, _ret2;
classCallCheck(this, ResultStore);
for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = arguments[_key2];
}
return _ret2 = (_temp2 = (_this2 = possibleConstructorReturn(this, _React$Component2.call.apply(_React$Component2, [this].concat(args))), _this2), _this2.reset = function () {
var execute = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
_this2.ref.reset(execute && _this2._execute);
}, _temp2), possibleConstructorReturn(_this2, _ret2);
}
ResultStore.prototype.render = function render() {
var _this3 = this;
return React.createElement(
RootConsumer,
null,
function (context) {
invariant(context, INVARIANT_MUST_BE_A_CHILD, ResultStore.displayName, rootDisplayName);
warning_1(!_this3.props.hasOwnProperty('reset'), WARNING_PROPERTY_RESET_IS_DEPRECATED);
_this3._execute = context.execute;
return React.createElement(ResultStoreInternal, _extends({ ref: function ref(_ref) {
return _this3.ref = _ref;
}, resetFn: _this3.reset }, _this3.props, context));
}
);
};
/**
* Resets result store to its initial state.
* @method
* @param {bool} [execute=true] Wether execute promise-returning function after resetting or not.
*/
return ResultStore;
}(React.Component);
ResultStore.defaultProps = {
reduce: function reduce(_, value) {
return value;
}
};
/**
* Type of children function for {@link AsyncCall.ResultStore.HasResult}
* @function HasResultChildrenFunction
* @param {object} params
* @param {any} params.result
* @returns {ReactNode} Should return rendered React component(s) depending on supplied params.
* @remark type definition
*/
/**
* @class HasResult
* @classdesc
* React Component. Renders its children whenever result store is not empty (has result).
* Property `children` must be a function with the only argument receiving object with the only field `result`.
* @example
* ```jsx
* <AsyncCall.ResultStore.HasResult>{({result}) => <pre>{JSON.stringify(result)}</pre>}</AsyncCall.ResultStore.HasResult>
* ```
* @property {HasResultChildrenFunction} children
* @static
* @extends {React.StatelessComponent}
* @memberof AsyncCall.ResultStore
*/
ResultStore.HasResult = createAsyncCallChild(Consumer, ResultStore.displayName, function (props, contextProps) {
return contextProps.hasResult && renderChildrenFn(props, { result: contextProps.result });
}, DISPLAY_NAME_HAS_RESULT, requiredFuncPropType);
/**
* Reset function
* @function ResetFunction
* @param {bool} [execute=false] Wether execute promise-returning function after resetting or not.
* @remark type definition
*/
/**
* Type of children function for {@link AsyncCall.ResultStore.Resetter}
* @function ResetterChildrenFunction
* @param {object} params
* @param {ResetFunction} params.reset Function for manual clearing of {@link AsyncCall.ResultStore}.
* @returns {ReactNode} Should return rendered React component(s) depending on supplied params.
* @remark type definition
*/
/**
* @class Resetter
* @classdesc
* React Component. Renders its children always. Property `children` must be a function with the only argument receiving an object
* with a function for manual reset of {@link AsyncCall.ResultStore}.
* @example
* ```jsx
* <AsyncCall.ResultStore.Resetter>{({ reset }) => <button onClick={reset}>Reset</button>}</AsyncCall.ResultStore.Resetter>
* ```
* @property {ResetterChildrenFunction} children
* @static
* @extends {React.StatelessComponent}
* @memberof AsyncCall.ResultStore
*/
ResultStore.Resetter = createAsyncCallChild(Consumer, ResultStore.displayName, function (props, contextProps) {
return renderChildrenFn(props, { reset: contextProps.reset });
}, DISPLAY_NAME_RESETTER, requiredFuncPropType);
return ResultStore;
};
/**
* Asynchronous function (aka asynchronous operation or promise-returning function)
* which returns promise based on supplied parameter.
* @example
* The function below returns result (`Promise` object) of getting user data from GitHub API by his/her GitHub login:
* ```
* const getGitHubUserData = userName => fetch(`https://api.github.com/users/${userName}`).then(data => data.json())
* ```
* @function AsyncFunction
* @param {any} params Parameters, based on which function should return promise.
* @returns {Promise} Promise object that represents asynchronous operation result.
* @remark type definition
*/
/**
* A factory function that creates React component class and binds async operation to it:
* ```jsx
* const Fetcher = createAsyncCallComponent(() => fetch('https://api.github.com/repositories').then(data => data.json()))
* ```
*
* After calling of this function you can use returned component and its static sub-components to hook async operation lifecycle:
*
* ```jsx
* // Start executing async operation on Fetcher mount
* <Fetcher>
* <Fetcher.Running>
* Renders only if async operation is executing
* </Fetcher.Running>
*
* <Fetcher.Resolved>
* {({ result }) => (
* <div>
* Renders if async operation has been executed successfully
* <pre>{JSON.stringify(result)}></pre>
* </div>
* )}}
* </Fetcher.Resolved>
*
* <Fetcher.Rejected>
* Renders only if async operation failed
* </Fetcher.Rejected>
* </Fetcher>
* ```
*
* `createAsyncCallComponent` is the only member exported by react-async-call package.
* @param {AsyncFunction} fn See [`AsyncFunction` signature]{@link AsyncFunction} for details.
* @param {String} [displayName="AsyncCall"] Component name (visible, for example, in React extension of Chrome Dev Tools).
* @returns {AsyncCall} Returns React [component class `AsyncCall`]{@link AsyncCall} for the further usage.
* This class contains extra component classes [`Running`]{@link AsyncCall.Running},
* [`Rejected`]{@link AsyncCall.Rejected}, [`Resolved`]{@link AsyncCall.Resolved},
* [`Completed`]{@link AsyncCall.Completed}, [`ResultStore`]{@link AsyncCall.ResultStore},
* [`Executor`]{@link AsyncCall.Executor} and [`State`]{@link AsyncCall.State} which can be used as children
* (direct or indirect) of `AsyncCall`.
* @function
*/
var createAsyncCallComponent = function createAsyncCallComponent(fn, displayName) {
var _createContext = createContext(),
Provider = _createContext.Provider,
Consumer = _createContext.Consumer;
var rootDisplayName = 'production' !== 'production';
/**
* Execute function
* @function ExecuteFunction
* @returns {void}
* @remark type definition
*/
/**
* Type of children function for {@link AsyncCall}
* @function AsyncCallChildrenFunction
* @param {object} params Represents current status of asynchronous operation.
* @param {boolean} params.running Indicates whether asynchronous operation is executing or not.
* @param {boolean} params.rejected Indicates whether asynchronous operation was failed when it was called last time.
* If `true`, result of promise rejection (error) can be found in the `params.rejectReason`.
* @param {boolean} params.resolved Indicates whether asynchronous operation was succeeded when it was called last time.
* If `true`, result of promise resolving can be found in the `params.result`.
* @param {any=}params.result Contains result of promise (returned by async function) resolving
* if function call was successful. `undefined` if asynchronous operation is running or promise was rejected.
* @param {any=} params.rejectReason Contains result of promise (returned by async function) rejection
* if function call was unsuccessful. `undefined` if asynchronous operation is running or promise was resolved.
* @param {ExecuteFunction} params.execute Function for manual execution of asynchronous operation.
* @returns {ReactNode} Should return rendered React component(s) depending on supplied params.
* @remark type definition
*/
/**
* @classdesc
* React Component. This class is returned by call of {@link createAsyncCallComponent} and this is the only way to get it.
* @property {ReactNode | AsyncCallChildrenFunction} children Property `children` can be either a ReactNode or
* a function that receives an object as its only argument and return ReactNode.
* `AsyncCall` component **always** renders its children. We recommend to use
* [the first form of using `children` property](https://github.com/kuzn-ilya/react-async-call/blob/master/README.md#basic-usage)
* and respond to async operation execution results using *static sub-components* (like [`Running`]{@link AsyncCall.Running},
* [`Rejected`]{@link AsyncCall.Rejected}, [`Resolved`]{@link AsyncCall.Resolved},
* [`Completed`]{@link AsyncCall.Completed}, [`ResultStore`]{@link AsyncCall.ResultStore},
* [`Executor`]{@link AsyncCall.Executor} and [`State`]{@link AsyncCall.State}).
* @property {any} params A value that will be passed as a single argument into async function. Every time when change of this property occurs,
* asyncronous operation restarts.
* @property {Boolean} [lazy=false] If `true`, component will not start execution of asynchronous operation during component mount phase
* and on `params` property change. You should start async operation manualy by calling [`execute` method]{@link #AsyncCall+execute}
* or via [`Executor`]{@link AsyncCall.Executor}.
* @extends {React.Component}
*/
var AsyncCall = function (_React$Component) {
inherits(AsyncCall, _React$Component);
function AsyncCall() {
var _temp, _this, _ret;
classCallCheck(this, AsyncCall);
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return _ret = (_temp = (_this = possibleConstructorReturn(this, _React$Component.call.apply(_React$Component, [this].concat(args))), _this), _this.state = {
running: true,
rejected: false,
resolved: false
}, _this._callQueryFunc = function (params) {
_this.setState({ running: true, rejected: false, resolved: false });
var promise = fn(params);
invariant(promise && promise.then && isFunction(promise.then), INVARIANT_FUNCTION_SHOULD_RETURN_PROMISE);
promise.then(function (result) {
if (_this._mounted) {
_this.setState({
running: false,
rejected: false,
resolved: true,
result: result
});
}
}, function (rejectReason) {
if (_this._mounted) {
_this.setState({ running: false, rejected: true, rejectReason: rejectReason });
}
});
}, _this._getState = function () {
var _this$state = _this.state,
running = _this$state.running,
resolved = _this$state.resolved,
rejected = _this$state.rejected,
result = _this$state.result,
rejectReason = _this$state.rejectReason;
result = resolved ? { result: result } : {};
rejectReason = rejected ? { rejectReason: rejectReason } : {};
return _extends({
running: running,
rejected: rejected,
resolved: resolved,
execute: _this.execute
}, result, rejectReason);
}, _this.execute = function () {
_this._callQueryFunc(_this.props.params);
}, _temp), possibleConstructorReturn(_this, _ret);
}
/**
* @class Running
* @classdesc
* React Component. Renders its children whenever async operation was started but is still executing. Otherwise renders nothing.
* @example
* ```jsx
* <AsyncCall.Running>Executing...</AsyncCall.Running>
* ```
* @property {ReactNode} children
* @static
* @extends {React.StatelessComponent}
* @memberof AsyncCall
*/
/**
* Type of children function for {@link AsyncCall.Resolved}
* @function ResolvedChildrenFunction
* @param {object}
* @param {any} params.result
* @returns {ReactNode} Should return rendered React component(s) depending on supplied params.
* @remark type definition
*/
/**
* @class Resolved
* @classdesc
* React Component. Renders its children whenever async operation has been completed successfully (promise was resolved),
* but is still not started again. Otherwise renders nothing.
* Property `children` can be either React node(s) or children function with the only argument receiving object with the only field `result`.
* @example
* ```jsx
* <AsyncCall.Resolved>Last async operation was successful!</AsyncCall.Resolved>
* ```
*
* or
*
* ```jsx
* <AsyncCall.Resolved>{({ result }) => <pre>{JSON.stringify(result)}</pre>}</AsyncCall.Resolved>
* ```
* @property {ReactNode | ResolvedChildrenFunction} children
* @static
* @extends {React.StatelessComponent}
* @memberof AsyncCall
*/
/**
* Type of children function for {@link AsyncCall.Rejected}
* @function RejectedChildrenFunction
* @param {object} params
* @param {any} params.rejectReason
* @returns {ReactNode} Should return rendered React component(s) depending on supplied params.
* @remark type definition
*/
/**
* @class Rejected
* @static
* @classdesc
* React Component. Renders its children whenever async operation has been completed with failure (promise was rejected),
* but is still not started again. Otherwise renders nothing.
* Property `children` can be either React node(s) or children function with the only argument receiving object with the only field `rejectReason`
* (promise reject reason).
* @example
* ```jsx
* <AsyncCall.Rejected>Error!</AsyncCall.Rejected>
* ```
*
* or
* ```jsx
* <AsyncCall.Rejected>{({ rejectReason }) => Error: {rejectReason}}</AsyncCall.Rejected>
* ```
* @property {ReactNode | RejectedChildrenFunction} children
* @extends {React.StatelessComponent}
* @memberof AsyncCall
*/
/**
* Type of children function for {@link AsyncCall.Executor}
* @function ExecutorChildrenFunction
* @param {object} params
* @param {ExecuteFunction} params.execute Function for manual execution of asynchronous operation.
* @returns {ReactNode} Should return rendered React component(s) depending on supplied params.
* @remark type definition
*/
/**
* @class Executor
* @classdesc
* React Component. Renders its children always. Property `children` must be a function with the only argument receiving an object
* with a function for manual execution of async operation.
* @example
* ```jsx
* <AsyncCall.Executor>{({ execute }) => <button onClick={execute}>Click me!</button>}</AsyncCall.Executor>
* ```
* @property {ExecutorChildrenFunction} children
* @static
* @extends {React.StatelessComponent}
* @memberof AsyncCall
*/
/**
* Type of children function for {@link AsyncCall.State}
* @function StateChildrenFunction
* @param {object} params
* @param {boolean} params.running Whether async opertation is executing or not.
* If you only need to process `running`, use either {@link AsyncCall.Running} or {@link AsyncCall.Completed} component instead.
* @param {boolean} params.resolved Whether async opertation was completed successfully last time or not.
* If you only need to process `resolved` and `result`, {@link AsyncCall.Resolved} component instead.
* @param {boolean} params.rejected Whether async opertation failed last time or not.
* If you only need to process `rejected` and `rejectedStatus`, use {@link AsyncCall.Rejected} component instead.
* @param {any=} params.rejectReason Contains reject reason if async opertation failed last time.
* If you only need to process `rejected` and `rejectedReason`, use {@link AsyncCall.Rejected} component instead.
* @param {any=} params.result Contains result of last successful async operation call.
* If you only need to process `resolved` and `result`, use {@link AsyncCall.Resolved} component instead.
* If you need to accumulate result, consider {@link AsyncCall.ResultStore} usage.
* @param {ExecuteFunction} params.execute Callback for manual execution of async operation.
* If you only need to execute async operation manualy, use {@link AsyncCall.Executor} component instead. |
* @returns {ReactNode} Should return rendered React component(s) depending on supplied params.
* @remark type definition
*/
/**
* @class State
* @classdesc
* React Component. Renders its children always. Property `children` must be a function
* with the only argument receiving an object ([see description of `StateChildrenFunction`]{@link StateChildrenFunction})
* with the state of async operation. `State` component is handy for complicated UI cases when none of static components of {@link AsyncCall} suits you.
* @example
* ```jsx
* <AsyncCall.State>
* {({ running, resolved, result, rejected, rejectReason, execute }) => {
* // Something that depends on all of the argument's properties
* }}
* </AsyncCall.State>
* ```
* @property {StateChildrenFunction} children
* @static
* @extends {React.StatelessComponent}
* @memberof AsyncCall
*/
/**
* @class Completed
* @classdesc
* React Component. Renders its children whenever async operation has been completed (successfully or not),
* but is still not started again. Otherwise renders nothing.
*
* ```jsx
* <AsyncCall.Completed>
* Async operation completed
* </AsyncCall.Completed>
* ```
* @property {ReactNode=} children React children to be rendered whenever async operation is completed.
* @static
* @extends {React.StatelessComponent}
* @memberof AsyncCall
*/
AsyncCall.prototype.componentDidMount = function componentDidMount() {
invariant(isFunction(fn), INVARIANT_FUNCTION_SHOULD_BE_PASSED, fn);
this._mounted = true;
var props = this.props;
if (!props.lazy) {
this._callQueryFunc(props.params);
}
};
AsyncCall.prototype.componentWillUnmount = function componentWillUnmount() {
this._mounted = false;
};
AsyncCall.prototype.componentDidUpdate = function componentDidUpdate(prevProps) {
var props = this.props;
var params = props.params;
if (!props.lazy && !shallowEqual_1(params, prevProps.params)) {
this._callQueryFunc(params);
}
};
/**
* Method for executing async operation manually.
* It is recommended to use [`<Executor>` component]{@link AsyncCall.Executor} instead.
* @method
*/
AsyncCall.prototype.render = function render() {
var state = this._getState();
var props = this.props;
return React.createElement(
Provider,
{ value: state },
props.children !== undefined && renderChildren(props, state)
);
};
return AsyncCall;
}(React.Component);
AsyncCall.Running = createAsyncCallChild(Consumer, rootDisplayName, function (props, contextProps) {
return contextProps.running && props.children;
}, DISPLAY_NAME_RUNNING, nodePropType);
AsyncCall.Resolved = createAsyncCallChild(Consumer, rootDisplayName, function (props, contextProps) {
return contextProps.resolved && renderChildren(props, { result: contextProps.result });
}, DISPLAY_NAME_RESOLVED, nodeOrFuncPropType);
AsyncCall.Rejected = createAsyncCallChild(Consumer, rootDisplayName, function (props, contextProps) {
return contextProps.rejected && renderChildren(props, { rejectReason: contextProps.rejectReason });
}, DISPLAY_NAME_REJECTED, nodeOrFuncPropType);
AsyncCall.Executor = createAsyncCallChild(Consumer, rootDisplayName, function (props, contextProps) {
return renderChildrenFn(props, { execute: contextProps.execute });
}, DISPLAY_NAME_EXECUTOR, requiredFuncPropType);
AsyncCall.State = createAsyncCallChild(Consumer, rootDisplayName, renderChildrenFn, DISPLAY_NAME_STATE, requiredFuncPropType);
AsyncCall.ResultStore = createResultStore(Consumer, rootDisplayName);
AsyncCall.Completed = createAsyncCallChild(Consumer, rootDisplayName, function (props, contextProps) {
return (contextProps.resolved || contextProps.rejected) && props.children;
}, DISPLAY_NAME_COMPLETED, nodePropType);
return AsyncCall;
};
exports.default = createAsyncCallComponent;
//# sourceMappingURL=react-async-call.production.js.map