mirror of
https://github.com/zhigang1992/react-native-web.git
synced 2026-03-27 01:34:17 +08:00
[change] call 'onLayout' when elements are resized
Uses ResizeObserver to monitor layout changes. Falls back to window.onresize (with initial firing). Fix #60 Close #848
This commit is contained in:
committed by
Nicolas Gallagher
parent
9427eea293
commit
5a04d07a35
@@ -9,6 +9,7 @@
|
||||
|
||||
import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
|
||||
import debounce from 'debounce';
|
||||
import findNodeHandle from '../../exports/findNodeHandle';
|
||||
|
||||
const emptyObject = {};
|
||||
const registry = {};
|
||||
@@ -16,22 +17,68 @@ const registry = {};
|
||||
let id = 1;
|
||||
const guid = () => `r-${id++}`;
|
||||
|
||||
let resizeObserver;
|
||||
if (canUseDOM) {
|
||||
const triggerAll = () => {
|
||||
Object.keys(registry).forEach(key => {
|
||||
const instance = registry[key];
|
||||
instance._handleLayout();
|
||||
if (typeof window.ResizeObserver !== 'undefined') {
|
||||
resizeObserver = new window.ResizeObserver(entries => {
|
||||
entries.forEach(({ target }) => {
|
||||
const instance = registry[target._onLayoutId];
|
||||
instance && instance._handleLayout();
|
||||
});
|
||||
});
|
||||
};
|
||||
} else {
|
||||
if (process.env.NODE_ENV !== 'production' && process.env.NODE_ENV !== 'test') {
|
||||
console.warn(
|
||||
'onLayout relies on ResizeObserver which is not supported by your browser. ' +
|
||||
'Please include a polyfill, e.g., https://github.com/que-etc/resize-observer-polyfill. ' +
|
||||
'Falling back to window.onresize.'
|
||||
);
|
||||
}
|
||||
|
||||
window.addEventListener('resize', debounce(triggerAll, 16), false);
|
||||
const triggerAll = () => {
|
||||
Object.keys(registry).forEach(key => {
|
||||
const instance = registry[key];
|
||||
instance._handleLayout();
|
||||
});
|
||||
};
|
||||
|
||||
window.addEventListener('resize', debounce(triggerAll, 16), false);
|
||||
}
|
||||
}
|
||||
|
||||
const observe = instance => {
|
||||
const id = guid();
|
||||
registry[id] = instance;
|
||||
|
||||
if (resizeObserver) {
|
||||
const node = findNodeHandle(instance);
|
||||
node._onLayoutId = id;
|
||||
resizeObserver.observe(node);
|
||||
} else {
|
||||
const id = guid();
|
||||
instance._onLayoutId = id;
|
||||
instance._handleLayout();
|
||||
}
|
||||
};
|
||||
|
||||
const unobserve = instance => {
|
||||
delete registry[instance._onLayoutId];
|
||||
if (resizeObserver) {
|
||||
const node = findNodeHandle(instance);
|
||||
delete node._onLayoutId;
|
||||
resizeObserver.unobserve(node);
|
||||
} else {
|
||||
delete instance._onLayoutId;
|
||||
}
|
||||
};
|
||||
|
||||
const safeOverride = (original, next) => {
|
||||
if (original) {
|
||||
return function prototypeOverride() {
|
||||
original.call(this);
|
||||
next.call(this);
|
||||
/* eslint-disable prefer-rest-params */
|
||||
original.call(this, arguments);
|
||||
next.call(this, arguments);
|
||||
/* eslint-enable prefer-rest-params */
|
||||
};
|
||||
}
|
||||
return next;
|
||||
@@ -47,16 +94,20 @@ const applyLayout = Component => {
|
||||
function componentDidMount() {
|
||||
this._layoutState = emptyObject;
|
||||
this._isMounted = true;
|
||||
this._onLayoutId = guid();
|
||||
registry[this._onLayoutId] = this;
|
||||
this._handleLayout();
|
||||
observe(this);
|
||||
}
|
||||
);
|
||||
|
||||
Component.prototype.componentDidUpdate = safeOverride(
|
||||
componentDidUpdate,
|
||||
function componentDidUpdate() {
|
||||
this._handleLayout();
|
||||
function componentDidUpdate(prevProps) {
|
||||
if (this.props.onLayout && !prevProps.onLayout) {
|
||||
observe(this);
|
||||
} else if (!this.props.onLayout && prevProps.onLayout) {
|
||||
unobserve(this);
|
||||
} else if (!resizeObserver) {
|
||||
this._handleLayout();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -64,7 +115,7 @@ const applyLayout = Component => {
|
||||
componentWillUnmount,
|
||||
function componentWillUnmount() {
|
||||
this._isMounted = false;
|
||||
delete registry[this._onLayoutId];
|
||||
unobserve(this);
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
|
||||
This guide will help you to use and test React Native for Web once it has been installed.
|
||||
|
||||
Your application may need to polyfill `Promise`, `Object.assign`, and
|
||||
`Array.from` as necessary for your desired browser support.
|
||||
Your application may need to polyfill `Promise`, `Object.assign`, `Array.from`,
|
||||
and [`ResizeObserver`](https://github.com/que-etc/resize-observer-polyfill) as
|
||||
necessary for your desired browser support.
|
||||
|
||||
## Adding to a new web app
|
||||
|
||||
|
||||
@@ -132,13 +132,13 @@ const TextScreen = () => (
|
||||
<DocItem
|
||||
name="onLayout"
|
||||
typeInfo="?function"
|
||||
description={
|
||||
description={[
|
||||
<AppText>
|
||||
Invoked on mount and layout changes with{' '}
|
||||
<Code>{'{ nativeEvent: { layout: { x, y, width, height } } }'}</Code>, where{' '}
|
||||
<Code>x</Code> and <Code>y</Code> are the offsets from the parent node.
|
||||
</AppText>
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
<DocItem
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import PropOnLayout from './examples/PropOnLayout';
|
||||
import PropPointerEvents from './examples/PropPointerEvents';
|
||||
import transformExamples from './examples/transforms';
|
||||
import ZIndexExample from './examples/ZIndex';
|
||||
@@ -132,6 +133,9 @@ const ViewScreen = () => (
|
||||
<Code>x</Code> and <Code>y</Code> are the offsets from the parent node.
|
||||
</AppText>
|
||||
}
|
||||
example={{
|
||||
render: () => <PropOnLayout />
|
||||
}}
|
||||
/>
|
||||
|
||||
<DocItem
|
||||
|
||||
76
website/storybook/1-components/View/examples/PropOnLayout.js
Normal file
76
website/storybook/1-components/View/examples/PropOnLayout.js
Normal file
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { StyleSheet, Text, View } from 'react-native';
|
||||
|
||||
const l1 = { width: '100%', paddingLeft: 0, paddingTop: 0 };
|
||||
const l2 = { width: '75%', paddingLeft: 10, paddingTop: 10 };
|
||||
|
||||
export default class ViewOnLayoutExample extends React.Component {
|
||||
state = {
|
||||
layoutInfo: {}
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this._layout = l1;
|
||||
|
||||
this._interval = setInterval(() => {
|
||||
if (this._ref) {
|
||||
this._ref.setNativeProps({ style: this._layout });
|
||||
this._layout = this._layout.width === '100%' ? l2 : l1;
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
clearInterval(this._interval);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { x, y, width, height } = this.state.layoutInfo;
|
||||
return (
|
||||
<View style={styles.root}>
|
||||
<View style={styles.left}>
|
||||
<Text>x: {x}</Text>
|
||||
<Text>y: {y}</Text>
|
||||
<Text>width: {width}</Text>
|
||||
<Text>height: {height}</Text>
|
||||
</View>
|
||||
<View style={styles.right}>
|
||||
<View ref={this._setRef} style={styles.container}>
|
||||
<View onLayout={this._handleLayout} style={styles.box} />
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_handleLayout = ({ nativeEvent }) => {
|
||||
this.setState(() => ({ layoutInfo: nativeEvent.layout }));
|
||||
};
|
||||
|
||||
_setRef = component => {
|
||||
this._ref = component;
|
||||
};
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
flexDirection: 'row'
|
||||
},
|
||||
container: {
|
||||
height: 50
|
||||
},
|
||||
left: {
|
||||
width: 100
|
||||
},
|
||||
right: {
|
||||
flex: 1
|
||||
},
|
||||
box: {
|
||||
backgroundColor: '#eee',
|
||||
flex: 1
|
||||
}
|
||||
});
|
||||
@@ -18,7 +18,7 @@ const Box = ({ pointerEvents }) => (
|
||||
</TouchableHighlight>
|
||||
);
|
||||
|
||||
const ViewStyleExample = () => (
|
||||
const ViewPointerEventsExample = () => (
|
||||
<View pointerEvents="box-none">
|
||||
<View pointerEvents="box-none" style={styles.container}>
|
||||
<Box pointerEvents="none" />
|
||||
@@ -43,4 +43,4 @@ const styles = StyleSheet.create({
|
||||
borderStyle: 'solid'
|
||||
}
|
||||
});
|
||||
export default ViewStyleExample;
|
||||
export default ViewPointerEventsExample;
|
||||
|
||||
Reference in New Issue
Block a user