Skip to content

Instantly share code, notes, and snippets.

@miracle2k
Created May 29, 2017 15:32
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save miracle2k/4ed8827d0ed1bd9419d4871c7e0492dc to your computer and use it in GitHub Desktop.
Save miracle2k/4ed8827d0ed1bd9419d4871c7e0492dc to your computer and use it in GitHub Desktop.
/**
* 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.
*
* @providesModule ReactRelayRefetchContainer
*
* @format
*/
'use strict';
var _extends3 = _interopRequireDefault(require('babel-runtime/helpers/extends'));
var _classCallCheck3 = _interopRequireDefault(require('babel-runtime/helpers/classCallCheck'));
var _possibleConstructorReturn3 = _interopRequireDefault(require('babel-runtime/helpers/possibleConstructorReturn'));
var _inherits3 = _interopRequireDefault(require('babel-runtime/helpers/inherits'));
var _keys2 = _interopRequireDefault(require('babel-runtime/core-js/object/keys'));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
var _require = require('./ReactRelayContainerProfiler'),
profileContainer = _require.profileContainer;
var _require2 = require('./RelayContainerUtils'),
getComponentName = _require2.getComponentName,
getReactComponent = _require2.getReactComponent;
var containerContextTypes = {
relay: require('./RelayPropTypes').Relay
};
/**
* Composes a React component class, returning a new class that intercepts
* props, resolving them with the provided fragments and subscribing for
* updates.
*/
function createContainerWithFragments(Component, fragments, taggedNode) {
var ComponentClass = getReactComponent(Component);
var componentName = getComponentName(Component);
var containerName = 'Relay(' + componentName + ')';
var Container = function (_React$Component) {
(0, _inherits3['default'])(Container, _React$Component);
function Container(props, context) {
(0, _classCallCheck3['default'])(this, Container);
var _this = (0, _possibleConstructorReturn3['default'])(this, _React$Component.call(this, props, context));
_this._handleFragmentDataUpdate = function () {
var profiler = require('./RelayProfiler').profile('ReactRelayRefetchContainer.handleFragmentDataUpdate');
_this.setState({ data: _this._resolver.resolve() }, profiler.stop);
};
_this._refetch = function (refetchVariables, renderVariables, callback, options) {
var _assertRelayContext = assertRelayContext(_this.context.relay),
environment = _assertRelayContext.environment,
rootVariables = _assertRelayContext.variables;
var fetchVariables = typeof refetchVariables === 'function' ? refetchVariables(_this._getFragmentVariables()) : refetchVariables;
fetchVariables = (0, _extends3['default'])({}, rootVariables, fetchVariables);
var fragmentVariables = renderVariables ? (0, _extends3['default'])({}, rootVariables, renderVariables) : fetchVariables;
var onNext = function onNext(response) {
if (!_this._pendingRefetch) {
// only call callback once per refetch
return;
}
// TODO t15106389: add helper utility for fetching more data
_this._pendingRefetch = null;
_this._relayContext = {
environment: _this.context.relay.environment,
variables: fragmentVariables
};
callback && callback();
_this._resolver.setVariables(fragmentVariables);
_this.setState({ data: _this._resolver.resolve() });
};
var onError = function onError(error) {
_this._pendingRefetch = null;
callback && callback(error);
};
var cacheConfig = options ? { force: !!options.force } : undefined;
var _this$context$relay$e = _this.context.relay.environment.unstable_internal,
createOperationSelector = _this$context$relay$e.createOperationSelector,
getOperation = _this$context$relay$e.getOperation;
var query = getOperation(taggedNode);
var operation = createOperationSelector(query, fetchVariables);
// Immediately retain the results of the query to prevent cached
// data from being evicted
var reference = environment.retain(operation.root);
_this._references.push(reference);
_this._localVariables = fetchVariables;
if (_this._pendingRefetch) {
_this._pendingRefetch.dispose();
}
var pendingRefetch = environment.streamQuery({
cacheConfig: cacheConfig,
onError: onError,
onNext: onNext,
operation: operation
});
_this._pendingRefetch = pendingRefetch;
return {
dispose: function dispose() {
// Disposing a refetch() call should always dispose the fetch itself,
// but should not clear this._pendingFetch unless the refetch() being
// cancelled is the most recent call.
pendingRefetch.dispose();
if (_this._pendingRefetch === pendingRefetch) {
_this._pendingRefetch = null;
}
}
};
};
var relay = assertRelayContext(context.relay);
var createFragmentSpecResolver = relay.environment.unstable_internal.createFragmentSpecResolver;
_this._localVariables = null;
_this._pendingRefetch = null;
_this._references = [];
_this._relayContext;
_this._resolver = createFragmentSpecResolver(relay, fragments, props, _this._handleFragmentDataUpdate);
_this._relayContext = {
environment: _this.context.relay.environment,
variables: _this.context.relay.variables,
}
_this.state = {
data: _this._resolver.resolve(),
relayProp: _this._buildRelayProp(relay)
};
return _this;
}
/**
* When new props are received, read data for the new props and subscribe
* for updates. Props may be the same in which case previous data and
* subscriptions can be reused.
*/
Container.prototype.componentWillReceiveProps = function componentWillReceiveProps(nextProps, nextContext) {
var context = require('fbjs/lib/nullthrows')(nextContext);
var relay = assertRelayContext(context.relay);
var _relay$environment$un = relay.environment.unstable_internal,
createFragmentSpecResolver = _relay$environment$un.createFragmentSpecResolver,
getDataIDsFromObject = _relay$environment$un.getDataIDsFromObject;
var prevIDs = getDataIDsFromObject(fragments, this.props);
var nextIDs = getDataIDsFromObject(fragments, nextProps);
// If the environment has changed or props point to new records then
// previously fetched data and any pending fetches no longer apply:
// - Existing references are on the old environment.
// - Pending fetches are for the previous records.
if (this.context.relay.environment !== relay.environment || !require('fbjs/lib/areEqual')(prevIDs, nextIDs)) {
this._release();
this._localVariables = null;
this._resolver = createFragmentSpecResolver(relay, fragments, nextProps, this._handleFragmentDataUpdate);
this.setState({ relayProp: this._buildRelayProp(relay) });
} else if (!this._localVariables) {
this._resolver.setProps(nextProps);
}
var data = this._resolver.resolve();
if (data !== this.state.data) {
this.setState({ data: data });
}
};
Container.prototype.componentWillUnmount = function componentWillUnmount() {
this._release();
};
Container.prototype.shouldComponentUpdate = function shouldComponentUpdate(nextProps, nextState, nextContext) {
// Short-circuit if any Relay-related data has changed
if (nextContext.relay !== this.context.relay || nextState.data !== this.state.data || nextState.relayProp !== this.state.relayProp) {
return true;
}
// Otherwise, for convenience short-circuit if all non-Relay props
// are scalar and equal
var keys = (0, _keys2['default'])(nextProps);
for (var ii = 0; ii < keys.length; ii++) {
var _key = keys[ii];
if (!fragments.hasOwnProperty(_key) && !require('./isScalarAndEqual')(nextProps[_key], this.props[_key])) {
return true;
}
}
return false;
};
Container.prototype._release = function _release() {
this._resolver.dispose();
this._references.forEach(function (disposable) {
return disposable.dispose();
});
this._references.length = 0;
if (this._pendingRefetch) {
this._pendingRefetch.dispose();
this._pendingRefetch = null;
}
};
Container.prototype._buildRelayProp = function _buildRelayProp(relay) {
return {
environment: relay.environment,
refetch: this._refetch,
variables: this._relayContext.variables
};
};
/**
* Render new data for the existing props/context.
*/
Container.prototype._getFragmentVariables = function _getFragmentVariables() {
var getVariablesFromObject = this.context.relay.environment.unstable_internal.getVariablesFromObject;
return getVariablesFromObject(this.context.relay.variables, fragments, this.props);
};
Container.prototype.render = function render() {
if (ComponentClass) {
return require('react').createElement(ComponentClass, (0, _extends3['default'])({}, this.props, this.state.data, {
ref: 'component' // eslint-disable-line react/no-string-refs
, relay: this.state.relayProp
}));
} else {
// Stateless functional, doesn't support `ref`
return require('react').createElement(Component, (0, _extends3['default'])({}, this.props, this.state.data, {
relay: this.state.relayProp
}));
}
};
Container.prototype.getChildContext = function getChildContext() {
return {relay: this._relayContext};
}
return Container;
}(require('react').Component);
profileContainer(Container, 'ReactRelayRefetchContainer');
Container.contextTypes = containerContextTypes;
Container.displayName = containerName;
return Container;
}
function assertRelayContext(relay) {
require('fbjs/lib/invariant')(require('./isRelayContext')(relay), 'ReactRelayRefetchContainer: Expected `context.relay` to be an object ' + 'conforming to the `RelayContext` interface, got `%s`.', relay);
return relay;
}
/**
* Wrap the basic `createContainer()` function with logic to adapt to the
* `context.relay.environment` in which it is rendered. Specifically, the
* extraction of the environment-specific version of fragments in the
* `fragmentSpec` is memoized once per environment, rather than once per
* instance of the container constructed/rendered.
*/
function createContainer(Component, fragmentSpec, taggedNode) {
const C = require('./buildReactRelayContainer')(Component, fragmentSpec, function (ComponentClass, fragments) {
return createContainerWithFragments(ComponentClass, fragments, taggedNode);
});
C.childContextTypes = containerContextTypes;
return C
}
module.exports = { createContainer: createContainer, createContainerWithFragments: createContainerWithFragments };
@tuanquynet
Copy link

@miracle2k: Sorry, for stupid question. How can I apply your fix?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment