UNPKG

terriajs

Version:

Geospatial data visualization platform.

97 lines (78 loc) 4.29 kB
'use strict'; import defined from 'terriajs-cesium/Source/Core/defined'; import knockout from 'terriajs-cesium/Source/ThirdParty/knockout'; import PureRenderMixin from 'react-addons-pure-render-mixin'; const ObserveModelMixin = { componentWillMount() { this.__observeModelChangeSubscriptions = undefined; const originalRender = this.render; this.render = function renderForObserveModelMixin() { const that = this; let isFirstRender = true; // Clean up the previous subscription, if any. disposeSubscription(that); // Ignore dependencies so that the parent component's render function does not // depend on the child component's render function. If it did, a change to a child // would trigger re-rendering of all its ancestors, which is wasteful. return knockout.ignoreDependencies(function() { const computed = knockout.computed(function() { // The first time our computed render function is called, pass through to the real // render and track dependencies along the way. Any time after that is a result of // a change in one of the properties that the render used. But if we re-evaluate // the render function there, we're stepping outside the normal React lifecycle. // Instead, unsubscribe from the computed observable and force an update of the // React component (which will create a new computed observable). if (isFirstRender) { isFirstRender = false; return originalRender.call(that); } }).extend({notify: 'always'}); that.__observeModelChangeSubscriptions = []; let updateForced = false; /** * Disposes of the subscription to this component and forces an update, which will create a new * computed observable. */ function disposeAndForceUpdate() { disposeSubscription(that); if (!updateForced) { updateForced = true; that.forceUpdate(); } } // We also need to update on change of anything in props with a __knockoutSubscribable property. This // property is added to arrays by knockout-es5 and is notified whenever the array is modified. // Without this, changes in an observable array passed as props won't trigger re-render of the component, // even if the array is used in rendering. This is because Knockout observable arrays don't note a // dependency when accessing individual elements of the array. for (const prop in that.props) { if (that.props.hasOwnProperty(prop)) { if (defined(that.props[prop]) && defined(that.props[prop].__knockoutSubscribable)) { that.__observeModelChangeSubscriptions.push(that.props[prop].__knockoutSubscribable.subscribe(disposeAndForceUpdate)); } } } that.__observeModelChangeSubscriptions.push(computed.subscribe(disposeAndForceUpdate)); return computed(); }); }; }, componentWillUnmount() { disposeSubscription(this); }, shouldComponentUpdate: PureRenderMixin.shouldComponentUpdate }; /** * Disposes of all subscriptions that a component currently has. * * @param component The component to find and dispose subscriptions on. */ function disposeSubscription(component) { if (defined(component.__observeModelChangeSubscriptions)) { for (let i = 0; i < component.__observeModelChangeSubscriptions.length; ++i) { component.__observeModelChangeSubscriptions[i].dispose(); } component.__observeModelChangeSubscriptions = undefined; } } module.exports = ObserveModelMixin;