Fix for long press state transition error in Touchable.js

Summary:
public

This diff fixes an occasional JS exception thrown by Touchable.js when it attempts to transitions to the RESPONDER_ACTIVE_LONG_PRESS_IN state from the RESPONDER_INACTIVE_PRESS_IN state.

Although I wasn't able to reproduce the error while testing, I was able to identify the likely cause: the LONG_PRESS_DETECTED state transition is triggered by a timer that is started on touch-down. This timer should be cancelled if the gesture is interrupted, however I identified a code path where the state can be changed to RESPONDER_INACTIVE_PRESS_IN without the longPressDelayTimeout being cancelled.

To fix this, I've added some logic to cancel the timer in that case. I've also added a test for the error scenario that will display a redbox in __DEV__ mode, but will fail gracefully in production mode.

Reviewed By: jingc

Differential Revision: D2709750

fb-gh-sync-id: aeea1a31de5e92eb394c2ea177f556b131d50790
This commit is contained in:
Nick Lockwood 2015-12-02 09:05:39 -08:00 committed by facebook-github-bot-6
parent 7424af6078
commit 7ab17e5ef3
1 changed files with 21 additions and 3 deletions

View File

@ -456,6 +456,11 @@ var TouchableMixin = {
pressExpandBottom;
if (isTouchWithinActive) {
this._receiveSignal(Signals.ENTER_PRESS_RECT, e);
var curState = this.state.touchable.touchState;
if (curState === States.RESPONDER_INACTIVE_PRESS_IN) {
// fix for t7967420
this._cancelLongPressDelayTimeout();
}
} else {
this._cancelLongPressDelayTimeout();
this._receiveSignal(Signals.LEAVE_PRESS_RECT, e);
@ -564,7 +569,20 @@ var TouchableMixin = {
_handleLongDelay: function(e) {
this.longPressDelayTimeout = null;
this._receiveSignal(Signals.LONG_PRESS_DETECTED, e);
var curState = this.state.touchable.touchState;
if (curState !== States.RESPONDER_ACTIVE_PRESS_IN &&
curState !== States.RESPONDER_ACTIVE_LONG_PRESS_IN) {
if (__DEV__) {
throw new Error(
'Attempted to transition from state `' + curState + '` to `' +
States.RESPONDER_ACTIVE_LONG_PRESS_IN + '`, which is not supported. ' +
'This is most likely due to `Touchable.longPressDelayTimeout` not ' +
'being cancelled.'
);
}
} else {
this._receiveSignal(Signals.LONG_PRESS_DETECTED, e);
}
},
/**
@ -577,13 +595,13 @@ var TouchableMixin = {
*/
_receiveSignal: function(signal, e) {
var curState = this.state.touchable.touchState;
if (!(Transitions[curState] && Transitions[curState][signal])) {
var nextState = Transitions[curState] && Transitions[curState][signal];
if (!nextState) {
throw new Error(
'Unrecognized signal `' + signal + '` or state `' + curState +
'` for Touchable responder `' + this.state.touchable.responderID + '`'
);
}
var nextState = Transitions[curState][signal];
if (nextState === States.ERROR) {
throw new Error(
'Touchable cannot transition from `' + curState + '` to `' + signal +