Skip to content

Commit

Permalink
Pressability: Explicitly Commit Configurations
Browse files Browse the repository at this point in the history
Summary:
Refactors `Pressability` so that updates to the configuration are now explicitly committed using `configure()`.

Previously, the configuration was updated implicitly because `Pressability` accepted a series of functions whose closures capture values (e.g. `this.props`). Although these changes typically happen when component instances are "atomically" updated by React, it is not a guarantee. For example, arbitrary instance variables could be used to configure `Pressability`, and they could be muted at any time.

This change makes updates to the configuration of `Pressability` more predictable.

Changelog:
[Internal]

Reviewed By: TheSavior

Differential Revision: D18742620

fbshipit-source-id: d2e96dd1e3643289daab2177199a29f80d17b0bc
  • Loading branch information
yungsters authored and facebook-github-bot committed Dec 16, 2019
1 parent ebdce9b commit 9b81232
Show file tree
Hide file tree
Showing 7 changed files with 265 additions and 282 deletions.
45 changes: 23 additions & 22 deletions Libraries/Components/Touchable/TouchableBounce.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@

'use strict';

import Pressability from '../../Pressability/Pressability.js';
import Pressability, {
type PressabilityConfig,
} from '../../Pressability/Pressability.js';
import {PressabilityDebugView} from '../../Pressability/PressabilityDebug.js';
import type {ViewStyleProp} from '../../StyleSheet/StyleSheet.js';
import TVTouchable from './TVTouchable.js';
Expand Down Expand Up @@ -39,21 +41,20 @@ class TouchableBounce extends React.Component<Props, State> {
_tvTouchable: ?TVTouchable;

state: State = {
pressability: new Pressability({
getHitSlop: () => this.props.hitSlop,
getLongPressDelayMS: () => {
if (this.props.delayLongPress != null) {
const maybeNumber = this.props.delayLongPress;
if (typeof maybeNumber === 'number') {
return maybeNumber;
}
}
return 500;
},
getPressDelayMS: () => this.props.delayPressIn,
getPressOutDelayMS: () => this.props.delayPressOut,
getPressRectOffset: () => this.props.pressRetentionOffset,
getTouchSoundDisabled: () => this.props.touchSoundDisabled,
pressability: new Pressability(this._createPressabilityConfig()),
scale: new Animated.Value(1),
};

_createPressabilityConfig(): PressabilityConfig {
return {
cancelable: !this.props.rejectResponderTermination,
disabled: this.props.disabled,
hitSlop: this.props.hitSlop,
delayLongPress: this.props.delayLongPress,
delayPressIn: this.props.delayPressIn,
delayPressOut: this.props.delayPressOut,
pressRectOffset: this.props.pressRetentionOffset,
android_disableSound: this.props.touchSoundDisabled,
onBlur: event => {
if (Platform.isTV) {
this._bounceTo(1, 0.4, 0);
Expand Down Expand Up @@ -115,12 +116,8 @@ class TouchableBounce extends React.Component<Props, State> {
this.props.onPressOut(event);
}
},
onResponderTerminationRequest: () =>
!this.props.rejectResponderTermination,
onStartShouldSetResponder: () => !this.props.disabled,
}),
scale: new Animated.Value(1),
};
};
}

_bounceTo(
toValue: number,
Expand Down Expand Up @@ -201,6 +198,10 @@ class TouchableBounce extends React.Component<Props, State> {
}
}

componentDidUpdate(prevProps: Props, prevState: State) {
this.state.pressability.configure(this._createPressabilityConfig());
}

componentWillUnmount(): void {
if (Platform.isTV) {
if (this._tvTouchable != null) {
Expand Down
47 changes: 24 additions & 23 deletions Libraries/Components/Touchable/TouchableHighlight.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@

'use strict';

import Pressability from '../../Pressability/Pressability.js';
import Pressability, {
type PressabilityConfig,
} from '../../Pressability/Pressability.js';
import {PressabilityDebugView} from '../../Pressability/PressabilityDebug.js';
import StyleSheet, {type ViewStyleProp} from '../../StyleSheet/StyleSheet.js';
import type {ColorValue} from '../../StyleSheet/StyleSheetTypes.js';
Expand Down Expand Up @@ -159,21 +161,21 @@ class TouchableHighlight extends React.Component<Props, State> {
_tvTouchable: ?TVTouchable;

state: State = {
pressability: new Pressability({
getHitSlop: () => this.props.hitSlop,
getLongPressDelayMS: () => {
if (this.props.delayLongPress != null) {
const maybeNumber = this.props.delayLongPress;
if (typeof maybeNumber === 'number') {
return maybeNumber;
}
}
return 500;
},
getPressDelayMS: () => this.props.delayPressIn,
getPressOutDelayMS: () => this.props.delayPressOut,
getPressRectOffset: () => this.props.pressRetentionOffset,
getTouchSoundDisabled: () => this.props.touchSoundDisabled,
pressability: new Pressability(this._createPressabilityConfig()),
extraStyles:
this.props.testOnly_pressed === true ? this._createExtraStyles() : null,
};

_createPressabilityConfig(): PressabilityConfig {
return {
cancelable: !this.props.rejectResponderTermination,
disabled: this.props.disabled,
hitSlop: this.props.hitSlop,
delayLongPress: this.props.delayLongPress,
delayPressIn: this.props.delayPressIn,
delayPressOut: this.props.delayPressOut,
pressRectOffset: this.props.pressRetentionOffset,
android_disableSound: this.props.touchSoundDisabled,
onBlur: event => {
if (Platform.isTV) {
this._hideUnderlay();
Expand Down Expand Up @@ -227,13 +229,8 @@ class TouchableHighlight extends React.Component<Props, State> {
this.props.onPressOut(event);
}
},
onResponderTerminationRequest: () =>
!this.props.rejectResponderTermination,
onStartShouldSetResponder: () => !this.props.disabled,
}),
extraStyles:
this.props.testOnly_pressed === true ? this._createExtraStyles() : null,
};
};
}

_createExtraStyles(): ExtraStyles {
return {
Expand Down Expand Up @@ -363,6 +360,10 @@ class TouchableHighlight extends React.Component<Props, State> {
}
}

componentDidUpdate(prevProps: Props, prevState: State) {
this.state.pressability.configure(this._createPressabilityConfig());
}

componentWillUnmount(): void {
this._isMounted = false;
if (this._hideTimeout != null) {
Expand Down
55 changes: 24 additions & 31 deletions Libraries/Components/Touchable/TouchableNativeFeedback.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@

'use strict';

import Pressability from '../../Pressability/Pressability.js';
import Pressability, {
type PressabilityConfig,
} from '../../Pressability/Pressability.js';
import {PressabilityDebugView} from '../../Pressability/PressabilityDebug.js';
import TVTouchable from './TVTouchable.js';
import typeof TouchableWithoutFeedback from './TouchableWithoutFeedback.js';
Expand Down Expand Up @@ -144,31 +146,21 @@ class TouchableNativeFeedback extends React.Component<Props, State> {
_tvTouchable: ?TVTouchable;

state: State = {
pressability: new Pressability({
getHitSlop: () => this.props.hitSlop,
getLongPressDelayMS: () => {
if (this.props.delayLongPress != null) {
const maybeNumber = this.props.delayLongPress;
if (typeof maybeNumber === 'number') {
return maybeNumber;
}
}
return 500;
},
getPressDelayMS: () => this.props.delayPressIn,
getPressOutDelayMS: () => this.props.delayPressOut,
getPressRectOffset: () => this.props.pressRetentionOffset,
getTouchSoundDisabled: () => this.props.touchSoundDisabled,
onLongPress: event => {
if (this.props.onLongPress != null) {
this.props.onLongPress(event);
}
},
onPress: event => {
if (this.props.onPress != null) {
this.props.onPress(event);
}
},
pressability: new Pressability(this._createPressabilityConfig()),
};

_createPressabilityConfig(): PressabilityConfig {
return {
cancelable: !this.props.rejectResponderTermination,
disabled: this.props.disabled,
hitSlop: this.props.hitSlop,
delayLongPress: this.props.delayLongPress,
delayPressIn: this.props.delayPressIn,
delayPressOut: this.props.delayPressOut,
pressRectOffset: this.props.pressRetentionOffset,
android_disableSound: this.props.touchSoundDisabled,
onLongPress: this.props.onLongPress,
onPress: this.props.onPress,
onPressIn: event => {
if (Platform.OS === 'android') {
this._dispatchPressedStateChange(true);
Expand All @@ -191,11 +183,8 @@ class TouchableNativeFeedback extends React.Component<Props, State> {
this.props.onPressOut(event);
}
},
onResponderTerminationRequest: () =>
!this.props.rejectResponderTermination,
onStartShouldSetResponder: () => !this.props.disabled,
}),
};
};
}

_dispatchPressedStateChange(pressed: boolean): void {
if (Platform.OS === 'android') {
Expand Down Expand Up @@ -312,6 +301,10 @@ class TouchableNativeFeedback extends React.Component<Props, State> {
}
}

componentDidUpdate(prevProps: Props, prevState: State) {
this.state.pressability.configure(this._createPressabilityConfig());
}

componentWillUnmount(): void {
if (Platform.isTV) {
if (this._tvTouchable != null) {
Expand Down
50 changes: 20 additions & 30 deletions Libraries/Components/Touchable/TouchableOpacity.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@

'use strict';

import Pressability from '../../Pressability/Pressability.js';
import Pressability, {
type PressabilityConfig,
} from '../../Pressability/Pressability.js';
import {PressabilityDebugView} from '../../Pressability/PressabilityDebug.js';
import TVTouchable from './TVTouchable.js';
import typeof TouchableWithoutFeedback from './TouchableWithoutFeedback.js';
Expand Down Expand Up @@ -134,20 +136,18 @@ class TouchableOpacity extends React.Component<Props, State> {

state: State = {
anim: new Animated.Value(this._getChildStyleOpacityWithDefault()),
pressability: new Pressability({
getHitSlop: () => this.props.hitSlop,
getLongPressDelayMS: () => {
if (this.props.delayLongPress != null) {
const maybeNumber = this.props.delayLongPress;
if (typeof maybeNumber === 'number') {
return maybeNumber;
}
}
return 500;
},
getPressDelayMS: () => this.props.delayPressIn,
getPressOutDelayMS: () => this.props.delayPressOut,
getPressRectOffset: () => this.props.pressRetentionOffset,
pressability: new Pressability(this._createPressabilityConfig()),
};

_createPressabilityConfig(): PressabilityConfig {
return {
cancelable: !this.props.rejectResponderTermination,
disabled: this.props.disabled,
hitSlop: this.props.hitSlop,
delayLongPress: this.props.delayLongPress,
delayPressIn: this.props.delayPressIn,
delayPressOut: this.props.delayPressOut,
pressRectOffset: this.props.pressRetentionOffset,
onBlur: event => {
if (Platform.isTV) {
this._opacityInactive(250);
Expand All @@ -164,16 +164,8 @@ class TouchableOpacity extends React.Component<Props, State> {
this.props.onFocus(event);
}
},
onLongPress: event => {
if (this.props.onLongPress != null) {
this.props.onLongPress(event);
}
},
onPress: event => {
if (this.props.onPress != null) {
this.props.onPress(event);
}
},
onLongPress: this.props.onLongPress,
onPress: this.props.onPress,
onPressIn: event => {
this._opacityActive(
event.dispatchConfig.registrationName === 'onResponderGrant'
Expand All @@ -190,11 +182,8 @@ class TouchableOpacity extends React.Component<Props, State> {
this.props.onPressOut(event);
}
},
onResponderTerminationRequest: () =>
!this.props.rejectResponderTermination,
onStartShouldSetResponder: () => !this.props.disabled,
}),
};
};
}

/**
* Animate the touchable to a new opacity.
Expand Down Expand Up @@ -292,6 +281,7 @@ class TouchableOpacity extends React.Component<Props, State> {
}

componentDidUpdate(prevProps: Props, prevState: State) {
this.state.pressability.configure(this._createPressabilityConfig());
if (this.props.disabled !== prevProps.disabled) {
this._opacityInactive(250);
}
Expand Down
Loading

0 comments on commit 9b81232

Please sign in to comment.