[change] onLayout improvements

1. Fires when window resizes
2. Guards against nodes being unmounted

Fix #397
Ref #60
This commit is contained in:
Nicolas Gallagher
2017-03-18 11:56:52 -07:00
parent 89ad493ce5
commit 808790505e
6 changed files with 84 additions and 34 deletions
-1
View File
@@ -25,7 +25,6 @@
"dependencies": {
"animated": "^0.2.0",
"array-find-index": "^1.0.2",
"asap": "^2.0.5",
"babel-runtime": "^6.23.0",
"debounce": "^1.0.0",
"deep-assign": "^2.0.0",
+6 -3
View File
@@ -6,11 +6,11 @@
* @flow
*/
import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
import debounce from 'debounce';
import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
import invariant from 'fbjs/lib/invariant';
const win = ExecutionEnvironment.canUseDOM ? window : { screen: {} };
const win = canUseDOM ? window : { screen: {} };
const dimensions = {};
@@ -38,6 +38,9 @@ class Dimensions {
}
Dimensions.set();
ExecutionEnvironment.canUseDOM && window.addEventListener('resize', debounce(Dimensions.set, 50));
if (canUseDOM) {
window.addEventListener('resize', debounce(Dimensions.set, 16), false);
}
module.exports = Dimensions;
+2 -2
View File
@@ -1,7 +1,7 @@
import asap from 'asap';
import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
import generateCss from './generateCss';
import hash from './hash';
import requestAnimationFrame from 'fbjs/lib/requestAnimationFrame';
import staticCss from './staticCss';
const emptyObject = {};
@@ -111,7 +111,7 @@ class StyleManager {
className = createClassName(prop, value);
this._addToCache(className, prop, value);
if (canUseDOM) {
asap(() => {
requestAnimationFrame(() => {
const sheet = this.mainSheet.sheet;
// avoid injecting if the rule already exists (e.g., server rendered, hot reload)
if (this.mainSheet.textContent.indexOf(className) === -1) {
+17 -12
View File
@@ -1,5 +1,5 @@
import asap from 'asap';
import CSSPropertyOperations from 'react-dom/lib/CSSPropertyOperations';
import requestAnimationFrame from 'fbjs/lib/requestAnimationFrame';
const getRect = node => {
const height = node.offsetHeight;
@@ -15,13 +15,15 @@ const getRect = node => {
};
const measureLayout = (node, relativeToNativeNode, callback) => {
asap(() => {
const relativeNode = relativeToNativeNode || node.parentNode;
const relativeRect = getRect(relativeNode);
const { height, left, top, width } = getRect(node);
const x = left - relativeRect.left;
const y = top - relativeRect.top;
callback(x, y, width, height, left, top);
requestAnimationFrame(() => {
const relativeNode = relativeToNativeNode || (node && node.parentNode);
if (node && relativeNode) {
const relativeRect = getRect(relativeNode);
const { height, left, top, width } = getRect(node);
const x = left - relativeRect.left;
const y = top - relativeRect.top;
callback(x, y, width, height, left, top);
}
});
};
@@ -43,13 +45,16 @@ const UIManager = {
},
measureInWindow(node, callback) {
const { height, left, top, width } = getRect(node);
callback(left, top, width, height);
requestAnimationFrame(() => {
if (node) {
const { height, left, top, width } = getRect(node);
callback(left, top, width, height);
}
});
},
measureLayout(node, relativeToNativeNode, onFail, onSuccess) {
const relativeTo = relativeToNativeNode || node.parentNode;
measureLayout(node, relativeTo, onSuccess);
measureLayout(node, relativeToNativeNode, onSuccess);
},
updateView(node, props, component /* only needed to surpress React errors in development */) {
+58 -15
View File
@@ -5,24 +5,66 @@
* @flow
*/
import emptyFunction from 'fbjs/lib/emptyFunction';
import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
import debounce from 'debounce';
const emptyObject = {};
const registry = {};
let id = 1;
const guid = () => `r-${id++}`;
if (canUseDOM) {
const triggerAll = () => {
Object.keys(registry).forEach(key => {
const instance = registry[key];
instance._handleLayout();
});
};
window.addEventListener('resize', debounce(triggerAll, 16), false);
}
const safeOverride = (original, next) => {
if (original) {
return function prototypeOverride() {
original.call(this);
next.call(this);
};
}
return next;
};
const applyLayout = Component => {
const componentDidMount = Component.prototype.componentDidMount || emptyFunction;
const componentDidUpdate = Component.prototype.componentDidUpdate || emptyFunction;
const componentDidMount = Component.prototype.componentDidMount;
const componentDidUpdate = Component.prototype.componentDidUpdate;
const componentWillUnmount = Component.prototype.componentWillUnmount;
Component.prototype.componentDidMount = function() {
componentDidMount.call(this);
this._layoutState = emptyObject;
this._handleLayout();
};
Component.prototype.componentDidMount = safeOverride(
componentDidMount,
function componentDidMount() {
this._layoutState = emptyObject;
this._isMounted = true;
this._onLayoutId = guid();
registry[this._onLayoutId] = this;
this._handleLayout();
}
);
Component.prototype.componentDidUpdate = function() {
componentDidUpdate.call(this);
this._handleLayout();
};
Component.prototype.componentDidUpdate = safeOverride(
componentDidUpdate,
function componentDidUpdate() {
this._handleLayout();
}
);
Component.prototype.componentWillUnmount = safeOverride(
componentWillUnmount,
function componentWillUnmount() {
this._isMounted = false;
delete registry[this._onLayoutId];
}
);
Component.prototype._handleLayout = function() {
const layout = this._layoutState;
@@ -30,13 +72,14 @@ const applyLayout = Component => {
if (onLayout) {
this.measure((x, y, width, height) => {
if (!this._isMounted) return;
if (
layout.x !== x || layout.y !== y || layout.width !== width || layout.height !== height
) {
const nextLayout = { x, y, width, height };
const nativeEvent = { layout: nextLayout };
this._layoutState = { x, y, width, height };
const nativeEvent = { layout: this._layoutState };
onLayout({ nativeEvent, timeStamp: Date.now() });
this._layoutState = nextLayout;
}
});
}
+1 -1
View File
@@ -303,7 +303,7 @@ arrify@^1.0.0, arrify@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
asap@^2.0.5, asap@~2.0.3:
asap@~2.0.3:
version "2.0.5"
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.5.tgz#522765b50c3510490e52d7dcfe085ef9ba96958f"