Update usages of findDOMNode to indicate that it returns null (#761)

Fix a bug in web animations that results in a crash due to this
This commit is contained in:
Brent Erickson 2018-07-26 18:18:28 -07:00 коммит произвёл Eric Traut
Родитель 7347886ab9
Коммит 18d4a2e5b1
12 изменённых файлов: 107 добавлений и 85 удалений

Просмотреть файл

@ -343,11 +343,13 @@ export class NavigatorImpl extends NavigatorBase<NavigatorState> {
}
private _updateDimensionsCache() {
const transitioner = ReactDOM.findDOMNode(this.refs['transitioner']) as HTMLElement;
this._dimensions = {
width: transitioner.offsetWidth,
height: transitioner.offsetHeight
};
const transitioner = ReactDOM.findDOMNode(this.refs['transitioner']) as HTMLElement|null;
if (transitioner) {
this._dimensions = {
width: transitioner.offsetWidth,
height: transitioner.offsetHeight
};
}
}
// Helper method to extract Navigator's Scene config from the route
@ -631,11 +633,13 @@ export class NavigatorImpl extends NavigatorBase<NavigatorState> {
// setNativeProps.
private _setNativeStyles(component: React.ReactInstance, currentStyles: any) {
// Grab the actual element from the DOM.
let element = ReactDOM.findDOMNode(component) as HTMLElement;
const flatStyles: RX.Types.ViewStyleRuleSet = _.isArray(currentStyles) ? _.flatten(currentStyles) : currentStyles;
let element = ReactDOM.findDOMNode(component) as HTMLElement|null;
if (element) {
const flatStyles: RX.Types.ViewStyleRuleSet = _.isArray(currentStyles) ? _.flatten(currentStyles) : currentStyles;
// Modify styles
_.assign(element.style, flatStyles);
// Modify styles
_.assign(element.style, flatStyles);
}
}
}

Просмотреть файл

@ -17,16 +17,18 @@ import Types = require('../common/Types');
class Video extends RX.Component<Types.VideoProps, {}> {
componentDidMount() {
// We need to manually install the onEnded handler because. React doesn't support this.
let videoDOM = ReactDOM.findDOMNode(this) as HTMLVideoElement;
videoDOM.onended = () => {
if (this.props.onEnded) {
this.props.onEnded();
}
};
let videoDOM = ReactDOM.findDOMNode(this) as HTMLVideoElement|null;
if (videoDOM) {
videoDOM.onended = () => {
if (this.props.onEnded) {
this.props.onEnded();
}
};
}
}
componentWillUnmount() {
let videoDOM = ReactDOM.findDOMNode(this) as HTMLVideoElement;
let videoDOM = ReactDOM.findDOMNode(this) as HTMLVideoElement|null;
if (videoDOM) {
// Prevent Chrome based browsers to leak video elements
videoDOM.src = '';
@ -85,35 +87,35 @@ class Video extends RX.Component<Types.VideoProps, {}> {
}
seek(position: number) {
let videoDOM = ReactDOM.findDOMNode(this) as HTMLVideoElement;
let videoDOM = ReactDOM.findDOMNode(this) as HTMLVideoElement|null;
if (videoDOM) {
videoDOM.currentTime = position;
}
}
seekPercent(percentage: number) {
let videoDOM = ReactDOM.findDOMNode(this) as HTMLVideoElement;
let videoDOM = ReactDOM.findDOMNode(this) as HTMLVideoElement|null;
if (videoDOM) {
videoDOM.currentTime = percentage * videoDOM.duration;
}
}
play() {
let videoDOM = ReactDOM.findDOMNode(this) as HTMLVideoElement;
let videoDOM = ReactDOM.findDOMNode(this) as HTMLVideoElement|null;
if (videoDOM) {
videoDOM.play();
}
}
pause() {
let videoDOM = ReactDOM.findDOMNode(this) as HTMLVideoElement;
let videoDOM = ReactDOM.findDOMNode(this) as HTMLVideoElement|null;
if (videoDOM) {
videoDOM.pause();
}
}
stop() {
let videoDOM = ReactDOM.findDOMNode(this) as HTMLVideoElement;
let videoDOM = ReactDOM.findDOMNode(this) as HTMLVideoElement|null;
if (videoDOM) {
videoDOM.pause();
videoDOM.currentTime = 0;
@ -121,7 +123,7 @@ class Video extends RX.Component<Types.VideoProps, {}> {
}
mute(muted: boolean) {
let videoDOM = ReactDOM.findDOMNode(this) as HTMLVideoElement;
let videoDOM = ReactDOM.findDOMNode(this) as HTMLVideoElement|null;
if (videoDOM) {
videoDOM.muted = muted;
}
@ -129,7 +131,7 @@ class Video extends RX.Component<Types.VideoProps, {}> {
private _onLoadedData = () => {
if (this.props.onLoadedData) {
let videoDOM = ReactDOM.findDOMNode(this) as HTMLVideoElement;
let videoDOM = ReactDOM.findDOMNode(this) as HTMLVideoElement|null;
if (videoDOM) {
const loadInfo: Types.VideoInfo = {
@ -146,7 +148,7 @@ class Video extends RX.Component<Types.VideoProps, {}> {
private _onTimeUpdate = () => {
if (this.props.onProgress) {
let videoDOM = ReactDOM.findDOMNode(this) as HTMLVideoElement;
let videoDOM = ReactDOM.findDOMNode(this) as HTMLVideoElement|null;
if (videoDOM) {
this.props.onProgress({
currentTime: videoDOM.currentTime,

Просмотреть файл

@ -395,14 +395,20 @@ function createAnimatedComponent<PropsType extends Types.CommonProps>(Component:
let attrib = this._findAnimatedAttributeByValue(this._animatedAttributes, valueObject);
if (attrib) {
let cssValue = this._generateCssAttributeValue(attrib, valueObject, valueObject._getValue());
(this._getDomNode().style as any)[attrib] = cssValue;
const domNode = this._getDomNode();
if (domNode) {
const cssValue = this._generateCssAttributeValue(attrib, valueObject, valueObject._getValue());
(domNode.style as any)[attrib] = cssValue;
}
return;
}
let transform = this._findAnimatedAttributeByValue(this._animatedTransforms, valueObject);
if (transform) {
this._getDomNode().style.transform = this._generateCssTransformList(true);
const domNode = this._getDomNode();
if (domNode) {
domNode.style.transform = this._generateCssTransformList(true);
}
}
}
@ -485,9 +491,12 @@ function createAnimatedComponent<PropsType extends Types.CommonProps>(Component:
// mapping the interpolated value in reverse. Instead, we'll
// simply update it to the "toValue".
if (!valueObject._isInterpolated()) {
let computedStyle = window.getComputedStyle(this._getDomNode(), undefined);
if (computedStyle && (computedStyle as any)[attrib]) {
partialValue = (computedStyle as any)[attrib];
const domNode = this._getDomNode();
if (domNode) {
let computedStyle = window.getComputedStyle(domNode, undefined);
if (computedStyle && (computedStyle as any)[attrib]) {
partialValue = (computedStyle as any)[attrib];
}
}
}
@ -524,8 +533,8 @@ function createAnimatedComponent<PropsType extends Types.CommonProps>(Component:
return partialValue;
}
private _getDomNode(): HTMLElement {
return ReactDOM.findDOMNode(this._mountedComponent) as HTMLElement;
private _getDomNode(): HTMLElement|null {
return ReactDOM.findDOMNode(this._mountedComponent) as HTMLElement|null;
}
// Looks for the specified value object in the specified map. Returns
@ -571,30 +580,33 @@ function createAnimatedComponent<PropsType extends Types.CommonProps>(Component:
}
if (activeTransitions.length > 0) {
executeTransition(this._getDomNode(), activeTransitions, () => {
// Clear all of the active transitions and invoke the onEnd callbacks.
let completeTransitions: ExtendedTransition[] = [];
const domNode = this._getDomNode();
if (domNode) {
executeTransition(domNode, activeTransitions, () => {
// Clear all of the active transitions and invoke the onEnd callbacks.
let completeTransitions: ExtendedTransition[] = [];
_.each(this._animatedAttributes, attrib => {
if (attrib.activeTransition) {
completeTransitions.push(attrib.activeTransition);
delete attrib.activeTransition;
}
});
_.each(this._animatedAttributes, attrib => {
if (attrib.activeTransition) {
completeTransitions.push(attrib.activeTransition);
delete attrib.activeTransition;
}
});
_.each(this._animatedTransforms, transform => {
if (transform.activeTransition) {
completeTransitions.push(transform.activeTransition);
delete transform.activeTransition;
}
});
_.each(this._animatedTransforms, transform => {
if (transform.activeTransition) {
completeTransitions.push(transform.activeTransition);
delete transform.activeTransition;
}
});
_.each(completeTransitions, transition => {
if (transition.onEnd) {
transition.onEnd({ finished: true });
}
_.each(completeTransitions, transition => {
if (transition.onEnd) {
transition.onEnd({ finished: true });
}
});
});
});
}
}
}

Просмотреть файл

@ -143,7 +143,7 @@ export class Button extends ButtonBase {
focus() {
if (this._isMounted) {
const el = ReactDOM.findDOMNode(this) as HTMLButtonElement;
const el = ReactDOM.findDOMNode(this) as HTMLButtonElement|null;
if (el) {
el.focus();
}
@ -152,7 +152,7 @@ export class Button extends ButtonBase {
blur() {
if (this._isMounted) {
const el = ReactDOM.findDOMNode(this) as HTMLButtonElement;
const el = ReactDOM.findDOMNode(this) as HTMLButtonElement|null;
if (el) {
el.blur();
}

Просмотреть файл

@ -104,7 +104,7 @@ export class Link extends React.Component<Types.LinkProps, Types.Stateless> {
focus() {
if (this._isMounted) {
const el = ReactDOM.findDOMNode(this) as HTMLAnchorElement;
const el = ReactDOM.findDOMNode(this) as HTMLAnchorElement|null;
if (el) {
el.focus();
}
@ -113,7 +113,7 @@ export class Link extends React.Component<Types.LinkProps, Types.Stateless> {
blur() {
if (this._isMounted) {
const el = ReactDOM.findDOMNode(this) as HTMLAnchorElement;
const el = ReactDOM.findDOMNode(this) as HTMLAnchorElement|null;
if (el) {
el.blur();
}

Просмотреть файл

@ -306,7 +306,8 @@ export class RootView extends React.Component<RootViewProps, RootViewState> {
}
protected _onMount = (component: PopupContainerView|null) => {
this._mountedComponent = component ? ReactDOM.findDOMNode(component) as HTMLElement : undefined;
const domNode = component && ReactDOM.findDOMNode(component) as HTMLElement|null;
this._mountedComponent = domNode ? domNode : undefined;
}
private _tryClosePopup = (e: MouseEvent) => {
@ -370,8 +371,8 @@ export class RootView extends React.Component<RootViewProps, RootViewState> {
}
private _determineIfClickOnElement(elementReference: React.Component<any, any>, eventSource: Element|null|undefined): boolean {
const element = ReactDOM.findDOMNode(elementReference) as HTMLElement;
const isClickOnElement = element && !!eventSource && element.contains(eventSource);
const element = ReactDOM.findDOMNode(elementReference) as HTMLElement|null;
const isClickOnElement = !!element && !!eventSource && element.contains(eventSource);
return isClickOnElement;
}
@ -591,7 +592,7 @@ export class RootView extends React.Component<RootViewProps, RootViewState> {
return;
}
let anchor = ReactDOM.findDOMNode(anchorComponent) as HTMLElement;
let anchor = ReactDOM.findDOMNode(anchorComponent) as HTMLElement|null;
// If the anchor has disappeared, dismiss the popup.
if (!anchor) {

Просмотреть файл

@ -226,7 +226,7 @@ export class ScrollView extends ViewBase<Types.ScrollViewProps, Types.Stateless>
this._customScrollbar = undefined;
}
let element = ReactDOM.findDOMNode(this) as HTMLElement;
let element = ReactDOM.findDOMNode(this) as HTMLElement|null;
if (element) {
this._customScrollbar = new CustomScrollbar(element);
const horizontalHidden = (props.horizontal && props.showsHorizontalScrollIndicator === false);

Просмотреть файл

@ -153,7 +153,7 @@ export class Text extends TextBase {
blur() {
if (this._isMounted) {
const el = ReactDOM.findDOMNode(this) as HTMLDivElement;
const el = ReactDOM.findDOMNode(this) as HTMLDivElement|null;
if (el) {
el.blur();
}
@ -170,7 +170,7 @@ export class Text extends TextBase {
focus() {
if (this._isMounted) {
const el = ReactDOM.findDOMNode(this) as HTMLDivElement;
const el = ReactDOM.findDOMNode(this) as HTMLDivElement|null;
if (el) {
el.focus();
}

Просмотреть файл

@ -30,7 +30,7 @@ export class UserInterface extends RX.UserInterface {
let deferred = SyncTasks.Defer<Types.LayoutInfo>();
const componentDomNode = ReactDOM.findDOMNode(component) as HTMLElement;
const componentDomNode = ReactDOM.findDOMNode(component) as HTMLElement|null;
if (!componentDomNode) {
deferred.reject('measureLayoutRelativeToWindow failed');
@ -53,8 +53,8 @@ export class UserInterface extends RX.UserInterface {
let deferred = SyncTasks.Defer<Types.LayoutInfo>();
const componentDomNode = ReactDOM.findDOMNode(component) as HTMLElement;
const ancestorDomNode = ReactDOM.findDOMNode(ancestor) as HTMLElement;
const componentDomNode = ReactDOM.findDOMNode(component) as HTMLElement|null;
const ancestorDomNode = ReactDOM.findDOMNode(ancestor) as HTMLElement|null;
if (!componentDomNode || !ancestorDomNode) {
deferred.reject('measureLayoutRelativeToAncestor failed');

Просмотреть файл

@ -157,7 +157,7 @@ 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;
const element = ReactDOM.findDOMNode(ref) as HTMLElement|null;
if (cur) {
delete this._resizeDetectorNodes[key];
@ -257,7 +257,7 @@ export class View extends ViewBase<Types.ViewProps, Types.Stateless> {
if (!this._isMounted) {
return null;
}
return ReactDOM.findDOMNode(this) as HTMLElement;
return ReactDOM.findDOMNode(this) as HTMLElement|null;
}
private _isHidden(): boolean {
@ -463,7 +463,7 @@ export class View extends ViewBase<Types.ViewProps, Types.Stateless> {
blur() {
if (this._isMounted) {
const el = ReactDOM.findDOMNode(this) as HTMLDivElement;
const el = ReactDOM.findDOMNode(this) as HTMLDivElement|null;
if (el) {
el.blur();
}
@ -480,7 +480,7 @@ export class View extends ViewBase<Types.ViewProps, Types.Stateless> {
focus() {
if (this._isMounted) {
const el = ReactDOM.findDOMNode(this) as HTMLDivElement;
const el = ReactDOM.findDOMNode(this) as HTMLDivElement|null;
if (el) {
el.focus();
}

Просмотреть файл

@ -35,7 +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) {
let domNode = ReactDOM.findDOMNode(move.element) as HTMLElement;
let domNode = ReactDOM.findDOMNode(move.element) as HTMLElement|null;
if (domNode) {
domNode.style.transform = 'translateY(' + -move.topDelta + 'px)';
@ -56,7 +56,7 @@ export class AnimateListEdits extends React.Component<AnimateListEditsProps, Typ
if (edits.moved.length > 0 && this.props.animateChildMove) {
edits.moved.forEach(function (move) {
counter++;
let domNode = ReactDOM.findDOMNode(move.element) as HTMLElement;
let domNode = ReactDOM.findDOMNode(move.element) as HTMLElement|null;
if (domNode) {
executeTransition(domNode, [{
property: 'transform',
@ -74,14 +74,17 @@ export class AnimateListEdits extends React.Component<AnimateListEditsProps, Typ
if (edits.added.length > 0 && this.props.animateChildEnter) {
edits.added.forEach(function (move) {
counter++;
executeTransition(ReactDOM.findDOMNode(move.element) as HTMLElement, [{
property: 'opacity',
from: 0,
to: 1,
delay: delay,
duration: 150,
timing: 'linear'
}], animationCompleted);
let domNode = ReactDOM.findDOMNode(move.element) as HTMLElement|null;
if (domNode) {
executeTransition(domNode, [{
property: 'opacity',
from: 0,
to: 1,
delay: delay,
duration: 150,
timing: 'linear'
}], animationCompleted);
}
});
}
animationCompleted();

Просмотреть файл

@ -92,21 +92,21 @@ export class FocusManager extends FocusManagerBase {
}
protected /* static */ addFocusListenerOnComponent(component: FocusableComponentInternal, onFocus: () => void): void {
const el = ReactDOM.findDOMNode(component) as HTMLElement;
const el = ReactDOM.findDOMNode(component) as HTMLElement|null;
if (el) {
el.addEventListener('focus', onFocus);
}
}
protected /* static */ removeFocusListenerFromComponent(component: FocusableComponentInternal, onFocus: () => void): void {
const el = ReactDOM.findDOMNode(component) as HTMLElement;
const el = ReactDOM.findDOMNode(component) as HTMLElement|null;
if (el) {
el.removeEventListener('focus', onFocus);
}
}
protected /* static */ focusComponent(component: FocusableComponentInternal): boolean {
const el = ReactDOM.findDOMNode(component) as HTMLElement;
const el = ReactDOM.findDOMNode(component) as HTMLElement|null;
if (el && el.focus) {
FocusManager.setLastFocusedProgrammatically(el);
el.focus();
@ -243,7 +243,7 @@ export class FocusManager extends FocusManagerBase {
const restrictionRemoved = newTabIndex === undefined;
if ((storedComponent.curTabIndex !== newTabIndex) || (storedComponent.curAriaHidden !== newAriaHidden)) {
const el = ReactDOM.findDOMNode(storedComponent.component) as HTMLElement;
const el = ReactDOM.findDOMNode(storedComponent.component) as HTMLElement|null;
if (el) {
if (storedComponent.curTabIndex !== newTabIndex) {
@ -365,7 +365,7 @@ export function applyFocusableComponentMixin(Component: any, isConditionallyFocu
if (origFocus) {
Component.prototype.focus = function () {
const el = ReactDOM.findDOMNode(this) as HTMLElement;
const el = ReactDOM.findDOMNode(this) as HTMLElement|null;
if (el) {
FocusManager.setLastFocusedProgrammatically(el);
}