зеркало из https://github.com/microsoft/reactxp.git
Fixed bug #875 - added exception handlers around calls to findDOMNode. We had previously assumed this call returned null if the node couldn't be found, but it turns out to throw an exception.
This commit is contained in:
Родитель
e8efbf656c
Коммит
fe406d0a53
|
@ -19,7 +19,7 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.3.18.tgz",
|
||||
"integrity": "sha512-aWTvLHzKqbVWCiee8huwf5x7Ob4n4gxDwgJT/X31HqjGVZpeUeFeSFYH5Gvi5Dmm5HKF+s+dQkwa/nnEVVzzzg==",
|
||||
"requires": {
|
||||
"csstype": "2.5.6"
|
||||
"csstype": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"@types/react-dom": {
|
||||
|
@ -27,8 +27,8 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.0.7.tgz",
|
||||
"integrity": "sha512-vaq4vMaJOaNgFff1t3LnHYr6vRa09vRspMkmLdXtFZmO1fwDI2snP+dpOkwrtlU8UC8qsqemCu4RmVM2OLq/fA==",
|
||||
"requires": {
|
||||
"@types/node": "10.9.2",
|
||||
"@types/react": "16.3.18"
|
||||
"@types/node": "*",
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"@types/react-native": {
|
||||
|
@ -36,7 +36,7 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.56.12.tgz",
|
||||
"integrity": "sha512-LrxtjunNc3p2hb3yPo4KCrzB4xxP14yqgrIhBIcHs+EK7w0m1mUqPKXUhQMbe0l/obZc4P+8q+ulXCWE47x0jw==",
|
||||
"requires": {
|
||||
"@types/react": "16.3.18"
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"ansi-regex": {
|
||||
|
@ -57,7 +57,7 @@
|
|||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"sprintf-js": "1.0.3"
|
||||
"sprintf-js": "~1.0.2"
|
||||
}
|
||||
},
|
||||
"assert": {
|
||||
|
@ -74,9 +74,9 @@
|
|||
"integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "1.1.3",
|
||||
"esutils": "2.0.2",
|
||||
"js-tokens": "3.0.2"
|
||||
"chalk": "^1.1.3",
|
||||
"esutils": "^2.0.2",
|
||||
"js-tokens": "^3.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"chalk": {
|
||||
|
@ -85,11 +85,11 @@
|
|||
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "2.2.1",
|
||||
"escape-string-regexp": "1.0.5",
|
||||
"has-ansi": "2.0.0",
|
||||
"strip-ansi": "3.0.1",
|
||||
"supports-color": "2.0.0"
|
||||
"ansi-styles": "^2.2.1",
|
||||
"escape-string-regexp": "^1.0.2",
|
||||
"has-ansi": "^2.0.0",
|
||||
"strip-ansi": "^3.0.0",
|
||||
"supports-color": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"js-tokens": {
|
||||
|
@ -112,7 +112,7 @@
|
|||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"balanced-match": "1.0.0",
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
|
@ -128,9 +128,9 @@
|
|||
"integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "3.2.1",
|
||||
"escape-string-regexp": "1.0.5",
|
||||
"supports-color": "5.5.0"
|
||||
"ansi-styles": "^3.2.1",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"supports-color": "^5.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
|
@ -139,7 +139,7 @@
|
|||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "1.9.2"
|
||||
"color-convert": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
|
@ -148,7 +148,7 @@
|
|||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "3.0.0"
|
||||
"has-flag": "^3.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -221,12 +221,12 @@
|
|||
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fs.realpath": "1.0.0",
|
||||
"inflight": "1.0.6",
|
||||
"inherits": "2.0.1",
|
||||
"minimatch": "3.0.4",
|
||||
"once": "1.4.0",
|
||||
"path-is-absolute": "1.0.1"
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^3.0.4",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"has-ansi": {
|
||||
|
@ -235,7 +235,7 @@
|
|||
"integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "2.1.1"
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"has-flag": {
|
||||
|
@ -250,8 +250,8 @@
|
|||
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"once": "1.4.0",
|
||||
"wrappy": "1.0.2"
|
||||
"once": "^1.3.0",
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"inherits": {
|
||||
|
@ -270,8 +270,8 @@
|
|||
"integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"argparse": "1.0.10",
|
||||
"esprima": "4.0.1"
|
||||
"argparse": "^1.0.7",
|
||||
"esprima": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
|
@ -284,7 +284,7 @@
|
|||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||
"requires": {
|
||||
"js-tokens": "4.0.0"
|
||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||
}
|
||||
},
|
||||
"minimatch": {
|
||||
|
@ -293,7 +293,7 @@
|
|||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"brace-expansion": "1.1.11"
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
},
|
||||
"object-assign": {
|
||||
|
@ -307,7 +307,7 @@
|
|||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"wrappy": "1.0.2"
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"path-is-absolute": {
|
||||
|
@ -327,8 +327,8 @@
|
|||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
|
||||
"integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==",
|
||||
"requires": {
|
||||
"loose-envify": "1.4.0",
|
||||
"object-assign": "4.1.1"
|
||||
"loose-envify": "^1.3.1",
|
||||
"object-assign": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"rebound": {
|
||||
|
@ -342,7 +342,7 @@
|
|||
"integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"path-parse": "1.0.6"
|
||||
"path-parse": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
|
@ -363,7 +363,7 @@
|
|||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "2.1.1"
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"subscribableevent": {
|
||||
|
@ -394,18 +394,18 @@
|
|||
"integrity": "sha1-mPMMAurjzecAYgHkwzywi0hYHu0=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"babel-code-frame": "6.26.0",
|
||||
"builtin-modules": "1.1.1",
|
||||
"chalk": "2.4.1",
|
||||
"commander": "2.17.1",
|
||||
"diff": "3.5.0",
|
||||
"glob": "7.1.2",
|
||||
"js-yaml": "3.12.0",
|
||||
"minimatch": "3.0.4",
|
||||
"resolve": "1.8.1",
|
||||
"semver": "5.5.1",
|
||||
"tslib": "1.9.3",
|
||||
"tsutils": "2.29.0"
|
||||
"babel-code-frame": "^6.22.0",
|
||||
"builtin-modules": "^1.1.1",
|
||||
"chalk": "^2.3.0",
|
||||
"commander": "^2.12.1",
|
||||
"diff": "^3.2.0",
|
||||
"glob": "^7.1.1",
|
||||
"js-yaml": "^3.7.0",
|
||||
"minimatch": "^3.0.4",
|
||||
"resolve": "^1.3.2",
|
||||
"semver": "^5.3.0",
|
||||
"tslib": "^1.8.0",
|
||||
"tsutils": "^2.27.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"tsutils": {
|
||||
|
@ -414,7 +414,7 @@
|
|||
"integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tslib": "1.9.3"
|
||||
"tslib": "^1.8.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -425,7 +425,7 @@
|
|||
"integrity": "sha512-PDYjvpo0gN9IfMULwKk0KpVOPMhU6cNoT9VwCOLeDl/QS8v8W2yspRpFFuUS7/c5EIH/n8ApMi8TxJAz1tfFUA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tsutils": "2.28.0"
|
||||
"tsutils": "^2.27.2 <2.29.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"tsutils": {
|
||||
|
@ -434,7 +434,7 @@
|
|||
"integrity": "sha512-bh5nAtW0tuhvOJnx1GLRn5ScraRLICGyJV5wJhtRWOLsxW70Kk5tZtpK3O/hW6LDnqKS9mlUMPZj9fEMJ0gxqA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tslib": "1.9.3"
|
||||
"tslib": "^1.8.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -445,7 +445,7 @@
|
|||
"integrity": "sha512-LjHBWR0vWAUHWdIAoTjoqi56Kz+FDKBgVEuL+gVPG/Pv7QW5IdaDDeK9Txlr6U0Cmckp5EgCIq1T25qe3J6hyw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tslib": "1.9.3"
|
||||
"tslib": "^1.8.1"
|
||||
}
|
||||
},
|
||||
"typescript": {
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
import * as PropTypes from 'prop-types';
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
|
||||
import { applyFocusableComponentMixin } from './utils/FocusManager';
|
||||
import { Button as ButtonBase } from '../common/Interfaces';
|
||||
|
@ -59,7 +58,7 @@ export class Button extends ButtonBase {
|
|||
hasRxButtonAscendant: PropTypes.bool
|
||||
};
|
||||
|
||||
private _isMounted = false;
|
||||
private _mountedButton: HTMLButtonElement|null = null;
|
||||
private _lastMouseDownEvent: Types.SyntheticEvent|undefined;
|
||||
private _ignoreClick = false;
|
||||
private _longPressTimer: number|undefined;
|
||||
|
@ -92,6 +91,7 @@ export class Button extends ButtonBase {
|
|||
// NOTE: We use tabIndex=0 to support focus.
|
||||
return (
|
||||
<button
|
||||
ref={ this._onMount }
|
||||
style={ this._getStyles() as any }
|
||||
role={ ariaRole }
|
||||
title={ this.props.title }
|
||||
|
@ -122,41 +122,33 @@ export class Button extends ButtonBase {
|
|||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._isMounted = true;
|
||||
|
||||
if (this.props.autoFocus) {
|
||||
this.requestFocus();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this._isMounted = false;
|
||||
}
|
||||
|
||||
requestFocus() {
|
||||
FocusArbitratorProvider.requestFocus(
|
||||
this,
|
||||
() => this.focus(),
|
||||
() => this._isMounted
|
||||
() => this._mountedButton !== null
|
||||
);
|
||||
}
|
||||
|
||||
focus() {
|
||||
if (this._isMounted) {
|
||||
const el = ReactDOM.findDOMNode(this) as HTMLButtonElement|null;
|
||||
if (el) {
|
||||
el.focus();
|
||||
}
|
||||
if (this._mountedButton) {
|
||||
this._mountedButton.focus();
|
||||
}
|
||||
}
|
||||
|
||||
blur() {
|
||||
if (this._isMounted) {
|
||||
const el = ReactDOM.findDOMNode(this) as HTMLButtonElement|null;
|
||||
if (el) {
|
||||
el.blur();
|
||||
if (this._mountedButton) {
|
||||
this._mountedButton.blur();
|
||||
}
|
||||
}
|
||||
|
||||
private _onMount = (ref: HTMLButtonElement|null) => {
|
||||
this._mountedButton = ref;
|
||||
}
|
||||
|
||||
protected onClick = (e: Types.MouseEvent) => {
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import * as PropTypes from 'prop-types';
|
||||
import { CSSProperties } from 'react';
|
||||
|
||||
|
@ -56,7 +55,7 @@ export class Link extends React.Component<Types.LinkProps, Types.Stateless> {
|
|||
|
||||
context!: LinkContext;
|
||||
|
||||
private _isMounted = false;
|
||||
private _mountedAnchor: HTMLAnchorElement|null = null;
|
||||
private _longPressTimer: number|undefined;
|
||||
|
||||
render() {
|
||||
|
@ -66,6 +65,7 @@ export class Link extends React.Component<Types.LinkProps, Types.Stateless> {
|
|||
// See: https://mathiasbynens.github.io/rel-noopener/
|
||||
return (
|
||||
<a
|
||||
ref={ this._onMount }
|
||||
style={ this._getStyles() }
|
||||
title={ this.props.title }
|
||||
href={ this.props.url }
|
||||
|
@ -86,44 +86,36 @@ export class Link extends React.Component<Types.LinkProps, Types.Stateless> {
|
|||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._isMounted = true;
|
||||
|
||||
if (this.props.autoFocus) {
|
||||
this.requestFocus();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this._isMounted = false;
|
||||
}
|
||||
|
||||
requestFocus() {
|
||||
FocusArbitratorProvider.requestFocus(
|
||||
this,
|
||||
() => this.focus(),
|
||||
() => this._isMounted
|
||||
() => this._mountedAnchor !== null
|
||||
);
|
||||
}
|
||||
|
||||
focus() {
|
||||
if (this._isMounted) {
|
||||
const el = ReactDOM.findDOMNode(this) as HTMLAnchorElement|null;
|
||||
if (el) {
|
||||
el.focus();
|
||||
}
|
||||
if (this._mountedAnchor) {
|
||||
this._mountedAnchor.focus();
|
||||
}
|
||||
}
|
||||
|
||||
blur() {
|
||||
if (this._isMounted) {
|
||||
const el = ReactDOM.findDOMNode(this) as HTMLAnchorElement|null;
|
||||
if (el) {
|
||||
el.blur();
|
||||
}
|
||||
if (this._mountedAnchor) {
|
||||
this._mountedAnchor.blur();
|
||||
}
|
||||
}
|
||||
|
||||
_getStyles(): CSSProperties {
|
||||
private _onMount = (ref: HTMLAnchorElement|null) => {
|
||||
this._mountedAnchor = ref;
|
||||
}
|
||||
|
||||
private _getStyles(): CSSProperties {
|
||||
// There's no way in HTML to properly handle numberOfLines > 1,
|
||||
// but we can correctly handle the common case where numberOfLines is 1.
|
||||
const ellipsisStyles = this.props.numberOfLines === 1 ? _styles.ellipsis : {};
|
||||
|
|
|
@ -85,7 +85,7 @@ if (typeof document !== 'undefined') {
|
|||
const style = document.createElement('style');
|
||||
style.type = 'text/css';
|
||||
style.appendChild(document.createTextNode(defaultBoxSizing));
|
||||
document.head.appendChild(style);
|
||||
document.head!.appendChild(style);
|
||||
}
|
||||
|
||||
export interface MainViewContext {
|
||||
|
@ -387,9 +387,14 @@ export class RootView extends React.Component<RootViewProps, RootViewState> {
|
|||
}
|
||||
|
||||
private _determineIfClickOnElement(elementReference: React.Component<any, any>, eventSource: Element|null|undefined): boolean {
|
||||
try {
|
||||
const element = ReactDOM.findDOMNode(elementReference) as HTMLElement|null;
|
||||
const isClickOnElement = !!element && !!eventSource && element.contains(eventSource);
|
||||
return isClickOnElement;
|
||||
} catch {
|
||||
// Exception is due to race condition with unmounting.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private _onMouseDownCapture = (e: MouseEvent) => {
|
||||
|
@ -600,7 +605,12 @@ export class RootView extends React.Component<RootViewProps, RootViewState> {
|
|||
return;
|
||||
}
|
||||
|
||||
let anchor = ReactDOM.findDOMNode(anchorComponent) as HTMLElement|null;
|
||||
let anchor: HTMLElement|null = null;
|
||||
try {
|
||||
anchor = ReactDOM.findDOMNode(anchorComponent) as HTMLElement|null;
|
||||
} catch {
|
||||
anchor = null;
|
||||
}
|
||||
|
||||
// If the anchor has disappeared, dismiss the popup.
|
||||
if (!anchor) {
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
|
||||
import * as _ from './utils/lodashMini';
|
||||
import * as RX from '../common/Interfaces';
|
||||
|
@ -233,9 +232,8 @@ export class ScrollView extends ViewBase<RX.Types.ScrollViewProps, RX.Types.Stat
|
|||
this._customScrollbar = undefined;
|
||||
}
|
||||
|
||||
let element = ReactDOM.findDOMNode(this) as HTMLElement|null;
|
||||
if (element) {
|
||||
this._customScrollbar = new CustomScrollbar(element);
|
||||
if (this._mountedComponent) {
|
||||
this._customScrollbar = new CustomScrollbar(this._mountedComponent);
|
||||
const horizontalHidden = (props.horizontal && props.showsHorizontalScrollIndicator === false);
|
||||
const verticalHidden = (props.vertical && props.showsVerticalScrollIndicator === false);
|
||||
this._customScrollbar.init({
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
import * as PropTypes from 'prop-types';
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
|
||||
import { FocusArbitratorProvider } from '../common/utils/AutoFocusHelper';
|
||||
import { Text as TextBase, Types } from '../common/Interfaces';
|
||||
|
@ -25,7 +24,7 @@ if (typeof document !== 'undefined') {
|
|||
const style = document.createElement('style');
|
||||
style.type = 'text/css';
|
||||
style.appendChild(document.createTextNode(textAsPseudoElement));
|
||||
document.head.appendChild(style);
|
||||
document.head!.appendChild(style);
|
||||
}
|
||||
|
||||
const _styles = {
|
||||
|
@ -66,7 +65,7 @@ export class Text extends TextBase {
|
|||
isRxParentAText: PropTypes.bool.isRequired
|
||||
};
|
||||
|
||||
private _isMounted = false;
|
||||
private _mountedText: HTMLDivElement|null = null;
|
||||
|
||||
getChildContext() {
|
||||
// Let descendant Types components know that their nearest Types ancestor is an Types.Text.
|
||||
|
@ -86,6 +85,7 @@ export class Text extends TextBase {
|
|||
if (this.props.selectable || typeof this.props.children !== 'string') {
|
||||
return (
|
||||
<div
|
||||
ref={ this._onMount }
|
||||
style={ this._getStyles() as any }
|
||||
aria-hidden={ isAriaHidden }
|
||||
onClick={ this.props.onPress }
|
||||
|
@ -102,6 +102,7 @@ export class Text extends TextBase {
|
|||
// will be displayed as pseudo element.
|
||||
return (
|
||||
<div
|
||||
ref={ this._onMount }
|
||||
style={ this._getStyles() as any }
|
||||
aria-hidden={ isAriaHidden }
|
||||
onClick={ this.props.onPress }
|
||||
|
@ -115,18 +116,16 @@ export class Text extends TextBase {
|
|||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._isMounted = true;
|
||||
|
||||
if (this.props.autoFocus) {
|
||||
this.requestFocus();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this._isMounted = false;
|
||||
private _onMount = (ref: HTMLDivElement|null) => {
|
||||
this._mountedText = ref;
|
||||
}
|
||||
|
||||
_getStyles(): Types.TextStyleRuleSet {
|
||||
private _getStyles(): Types.TextStyleRuleSet {
|
||||
// There's no way in HTML to properly handle numberOfLines > 1,
|
||||
// but we can correctly handle the common case where numberOfLines is 1.
|
||||
let combinedStyles = Styles.combine([this.props.numberOfLines === 1 ?
|
||||
|
@ -156,11 +155,8 @@ export class Text extends TextBase {
|
|||
}
|
||||
|
||||
blur() {
|
||||
if (this._isMounted) {
|
||||
const el = ReactDOM.findDOMNode(this) as HTMLDivElement|null;
|
||||
if (el) {
|
||||
el.blur();
|
||||
}
|
||||
if (this._mountedText) {
|
||||
this._mountedText.blur();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -168,16 +164,13 @@ export class Text extends TextBase {
|
|||
FocusArbitratorProvider.requestFocus(
|
||||
this,
|
||||
() => this.focus(),
|
||||
() => this._isMounted
|
||||
() => this._mountedText !== null
|
||||
);
|
||||
}
|
||||
|
||||
focus() {
|
||||
if (this._isMounted) {
|
||||
const el = ReactDOM.findDOMNode(this) as HTMLDivElement|null;
|
||||
if (el) {
|
||||
el.focus();
|
||||
}
|
||||
if (this._mountedText) {
|
||||
this._mountedText.focus();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,8 +28,13 @@ export class UserInterface extends RX.UserInterface {
|
|||
SyncTasks.Promise<RX.Types.LayoutInfo> {
|
||||
|
||||
let deferred = SyncTasks.Defer<RX.Types.LayoutInfo>();
|
||||
let componentDomNode: HTMLElement | null = null;
|
||||
|
||||
const componentDomNode = ReactDOM.findDOMNode(component) as HTMLElement|null;
|
||||
try {
|
||||
componentDomNode = ReactDOM.findDOMNode(component) as HTMLElement | null;
|
||||
} catch {
|
||||
// Component is no longer mounted.
|
||||
}
|
||||
|
||||
if (!componentDomNode) {
|
||||
deferred.reject('measureLayoutRelativeToWindow failed');
|
||||
|
@ -51,9 +56,15 @@ export class UserInterface extends RX.UserInterface {
|
|||
ancestor: React.Component<any, any>) : SyncTasks.Promise<RX.Types.LayoutInfo> {
|
||||
|
||||
let deferred = SyncTasks.Defer<RX.Types.LayoutInfo>();
|
||||
let componentDomNode: HTMLElement | null = null;
|
||||
let ancestorDomNode: HTMLElement | null = null;
|
||||
|
||||
const componentDomNode = ReactDOM.findDOMNode(component) as HTMLElement|null;
|
||||
const ancestorDomNode = ReactDOM.findDOMNode(ancestor) as HTMLElement|null;
|
||||
try {
|
||||
componentDomNode = ReactDOM.findDOMNode(component) as HTMLElement | null;
|
||||
ancestorDomNode = ReactDOM.findDOMNode(ancestor) as HTMLElement | null;
|
||||
} catch {
|
||||
// Components are no longer mounted.
|
||||
}
|
||||
|
||||
if (!componentDomNode || !ancestorDomNode) {
|
||||
deferred.reject('measureLayoutRelativeToAncestor failed');
|
||||
|
|
|
@ -68,7 +68,7 @@ if (typeof document !== 'undefined') {
|
|||
const style = document.createElement('style');
|
||||
style.type = 'text/css';
|
||||
style.appendChild(document.createTextNode(ignorePointerEvents));
|
||||
head.appendChild(style);
|
||||
head!.appendChild(style);
|
||||
}
|
||||
|
||||
export interface ViewContext {
|
||||
|
@ -157,7 +157,13 @@ export class View extends ViewBase<Types.ViewProps, Types.Stateless> {
|
|||
|
||||
let initResizer = (key: 'grow' | 'shrink', ref: any) => {
|
||||
const cur: HTMLElement | undefined = this._resizeDetectorNodes[key];
|
||||
const element = ReactDOM.findDOMNode(ref) as HTMLElement|null;
|
||||
let element: HTMLElement|null = null;
|
||||
|
||||
try {
|
||||
element = ReactDOM.findDOMNode(ref) as HTMLElement|null;
|
||||
} catch {
|
||||
// Swallow exception due to component unmount race condition.
|
||||
}
|
||||
|
||||
if (cur) {
|
||||
delete this._resizeDetectorNodes[key];
|
||||
|
@ -257,7 +263,12 @@ export class View extends ViewBase<Types.ViewProps, Types.Stateless> {
|
|||
if (!this._isMounted) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return ReactDOM.findDOMNode(this) as HTMLElement|null;
|
||||
} catch {
|
||||
// Handle exception due to potential unmount race condition.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private _isHidden(): boolean {
|
||||
|
@ -467,10 +478,14 @@ export class View extends ViewBase<Types.ViewProps, Types.Stateless> {
|
|||
|
||||
blur() {
|
||||
if (this._isMounted) {
|
||||
try {
|
||||
const el = ReactDOM.findDOMNode(this) as HTMLDivElement|null;
|
||||
if (el) {
|
||||
el.blur();
|
||||
}
|
||||
} catch {
|
||||
// Handle exception due to potential unmount race condition.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -484,10 +499,14 @@ export class View extends ViewBase<Types.ViewProps, Types.Stateless> {
|
|||
|
||||
focus() {
|
||||
if (this._isMounted) {
|
||||
try {
|
||||
const el = ReactDOM.findDOMNode(this) as HTMLDivElement|null;
|
||||
if (el) {
|
||||
el.focus();
|
||||
}
|
||||
} catch {
|
||||
// Handle exception due to potential unmount race condition.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ export class AnimateListEdits extends React.Component<AnimateListEditsProps, Typ
|
|||
let delay = 0;
|
||||
if (edits.removed.length > 0 && this.props.animateChildLeave) {
|
||||
edits.removed.forEach(function (move) {
|
||||
try {
|
||||
let domNode = ReactDOM.findDOMNode(move.element) as HTMLElement|null;
|
||||
if (domNode) {
|
||||
domNode.style.transform = 'translateY(' + -move.topDelta + 'px)';
|
||||
|
@ -49,6 +50,9 @@ export class AnimateListEdits extends React.Component<AnimateListEditsProps, Typ
|
|||
timing: 'linear'
|
||||
}], animationCompleted);
|
||||
}
|
||||
} catch {
|
||||
// Exception probably due to race condition in unmounting. Ignore.
|
||||
}
|
||||
});
|
||||
delay += 75;
|
||||
}
|
||||
|
@ -56,6 +60,8 @@ export class AnimateListEdits extends React.Component<AnimateListEditsProps, Typ
|
|||
if (edits.moved.length > 0 && this.props.animateChildMove) {
|
||||
edits.moved.forEach(function (move) {
|
||||
counter++;
|
||||
|
||||
try {
|
||||
let domNode = ReactDOM.findDOMNode(move.element) as HTMLElement|null;
|
||||
if (domNode) {
|
||||
executeTransition(domNode, [{
|
||||
|
@ -67,6 +73,9 @@ export class AnimateListEdits extends React.Component<AnimateListEditsProps, Typ
|
|||
timing: 'ease-out'
|
||||
}], animationCompleted);
|
||||
}
|
||||
} catch {
|
||||
// Exception probably due to race condition in unmounting. Ignore.
|
||||
}
|
||||
});
|
||||
}
|
||||
delay += 75;
|
||||
|
@ -74,6 +83,8 @@ export class AnimateListEdits extends React.Component<AnimateListEditsProps, Typ
|
|||
if (edits.added.length > 0 && this.props.animateChildEnter) {
|
||||
edits.added.forEach(function (move) {
|
||||
counter++;
|
||||
|
||||
try {
|
||||
let domNode = ReactDOM.findDOMNode(move.element) as HTMLElement|null;
|
||||
if (domNode) {
|
||||
executeTransition(domNode, [{
|
||||
|
@ -85,6 +96,9 @@ export class AnimateListEdits extends React.Component<AnimateListEditsProps, Typ
|
|||
timing: 'linear'
|
||||
}], animationCompleted);
|
||||
}
|
||||
} catch {
|
||||
// Exception probably due to race condition in unmounting. Ignore.
|
||||
}
|
||||
});
|
||||
}
|
||||
animationCompleted();
|
||||
|
|
|
@ -93,27 +93,39 @@ export class FocusManager extends FocusManagerBase {
|
|||
});
|
||||
}
|
||||
|
||||
protected /* static */ addFocusListenerOnComponent(component: FocusableComponentInternal, onFocus: () => void): void {
|
||||
protected addFocusListenerOnComponent(component: FocusableComponentInternal, onFocus: () => void): void {
|
||||
try {
|
||||
const el = ReactDOM.findDOMNode(component) as HTMLElement | null;
|
||||
if (el) {
|
||||
el.addEventListener('focus', onFocus);
|
||||
}
|
||||
} catch {
|
||||
// Swallow exception due to component unmount race condition.
|
||||
}
|
||||
}
|
||||
|
||||
protected /* static */ removeFocusListenerFromComponent(component: FocusableComponentInternal, onFocus: () => void): void {
|
||||
protected removeFocusListenerFromComponent(component: FocusableComponentInternal, onFocus: () => void): void {
|
||||
try {
|
||||
const el = ReactDOM.findDOMNode(component) as HTMLElement|null;
|
||||
if (el) {
|
||||
el.removeEventListener('focus', onFocus);
|
||||
}
|
||||
} catch {
|
||||
// Swallow exception due to component unmount race condition.
|
||||
}
|
||||
}
|
||||
|
||||
protected /* static */ focusComponent(component: FocusableComponentInternal): boolean {
|
||||
protected focusComponent(component: FocusableComponentInternal): boolean {
|
||||
try {
|
||||
const el = ReactDOM.findDOMNode(component) as HTMLElement|null;
|
||||
if (el && el.focus) {
|
||||
FocusManager.setLastFocusedProgrammatically(el);
|
||||
el.focus();
|
||||
return true;
|
||||
}
|
||||
} catch {
|
||||
// Swallow exception due to component unmount race condition.
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -367,10 +379,14 @@ export function applyFocusableComponentMixin(Component: any, isConditionallyFocu
|
|||
|
||||
if (origFocus) {
|
||||
Component.prototype.focus = function () {
|
||||
try {
|
||||
const el = ReactDOM.findDOMNode(this) as HTMLElement|null;
|
||||
if (el) {
|
||||
FocusManager.setLastFocusedProgrammatically(el);
|
||||
}
|
||||
} catch {
|
||||
// Swallow exception due to component unmount race condition.
|
||||
}
|
||||
|
||||
origFocus.apply(this, arguments);
|
||||
};
|
||||
|
|
Загрузка…
Ссылка в новой задаче