fix: emit didFocus and didBlur events based on parent's transition

This commit is contained in:
Satyajit Sahoo
2020-03-28 16:04:06 +01:00
parent c9313a1419
commit 14a6538cc8
2 changed files with 118 additions and 20 deletions

View File

@@ -51,12 +51,24 @@ export default class NavigationEventManager extends React.Component {
'willBlur',
this._handleWillBlur
);
this._didFocusSubscription = navigation.addListener(
'didFocus',
this._handleDidFocus
);
this._didBlurSubscription = navigation.addListener(
'didBlur',
this._handleDidBlur
);
}
componentWillUnmount() {
this._actionSubscription?.remove();
this._willFocusSubscription?.remove();
this._willBlurSubscription?.remove();
this._didFocusSubscription?.remove();
this._didBlurSubscription?.remove();
}
/**
@@ -74,6 +86,16 @@ export default class NavigationEventManager extends React.Component {
*/
_willBlurSubscription;
/**
* @type {{ remove(): void } | undefined}
*/
_didFocusSubscription;
/**
* @type {{ remove(): void } | undefined}
*/
_didBlurSubscription;
/**
* @type {string | undefined}
*/
@@ -84,6 +106,16 @@ export default class NavigationEventManager extends React.Component {
*/
_lastWillFocusKey;
/**
* @type {string | undefined}
*/
_lastDidBlurKey;
/**
* @type {string | undefined}
*/
_lastDidFocusKey;
/**
* The 'action' event will fire when navigation state changes.
* Detect if the focused route changed here and emit appropriate events.
@@ -113,10 +145,10 @@ export default class NavigationEventManager extends React.Component {
};
if (previous?.key !== current.key) {
this._emitFocus(current.key, payload);
this._emitWillFocus(current.key, payload);
if (previous?.key) {
this._emitBlur(previous.key, payload);
this._emitWillBlur(previous.key, payload);
}
}
@@ -125,11 +157,11 @@ export default class NavigationEventManager extends React.Component {
state.isTransitioning === false
) {
if (this._lastWillBlurKey) {
onEvent(this._lastWillBlurKey, 'didBlur', payload);
this._emitDidBlur(this._lastWillBlurKey, payload);
}
if (this._lastWillFocusKey) {
onEvent(this._lastWillFocusKey, 'didFocus', payload);
this._emitDidFocus(this._lastWillFocusKey, payload);
}
}
@@ -143,7 +175,7 @@ export default class NavigationEventManager extends React.Component {
const { navigation } = this.props;
const route = navigation.state.routes[navigation.state.index];
this._emitFocus(route.key, {
this._emitWillFocus(route.key, {
context: `${route.key}:${action.type}_${context || 'Root'}`,
state: route,
lastState: lastState?.routes?.find((r) => r.key === route.key),
@@ -159,7 +191,7 @@ export default class NavigationEventManager extends React.Component {
const { navigation } = this.props;
const route = navigation.state.routes[navigation.state.index];
this._emitBlur(route.key, {
this._emitWillBlur(route.key, {
context: `${route.key}:${action.type}_${context || 'Root'}`,
state: route,
lastState: lastState?.routes?.find((r) => r.key === route.key),
@@ -168,11 +200,49 @@ export default class NavigationEventManager extends React.Component {
});
};
/**
* @param {ParentPayload} payload
*/
_handleDidFocus = ({ lastState, state, action, context, type }) => {
if (this._lastWillFocusKey) {
const route = state.routes.find((r) => r.key === this._lastWillFocusKey);
if (route) {
this._emitDidFocus(route.key, {
context: `${route.key}:${action.type}_${context || 'Root'}`,
state: route,
lastState: lastState?.routes?.find((r) => r.key === route.key),
action,
type,
});
}
}
};
/**
* @param {ParentPayload} payload
*/
_handleDidBlur = ({ lastState, state, action, context, type }) => {
if (this._lastWillBlurKey) {
const route = state.routes.find((r) => r.key === this._lastWillBlurKey);
if (route) {
this._emitDidBlur(route.key, {
context: `${route.key}:${action.type}_${context || 'Root'}`,
state: route,
lastState: lastState?.routes?.find((r) => r.key === route.key),
action,
type,
});
}
}
};
/**
* @param {string} target
* @param {Payload} payload
*/
_emitFocus = (target, payload) => {
_emitWillFocus = (target, payload) => {
if (this._lastWillBlurKey === target) {
this._lastWillBlurKey = undefined;
}
@@ -181,6 +251,7 @@ export default class NavigationEventManager extends React.Component {
return;
}
this._lastDidFocusKey = undefined;
this._lastWillFocusKey = target;
const { navigation, onEvent } = this.props;
@@ -188,7 +259,7 @@ export default class NavigationEventManager extends React.Component {
onEvent(target, 'willFocus', payload);
if (typeof navigation.state.isTransitioning !== 'boolean') {
onEvent(target, 'didFocus', payload);
this._emitDidFocus(target, payload);
}
};
@@ -196,7 +267,7 @@ export default class NavigationEventManager extends React.Component {
* @param {string} target
* @param {Payload} payload
*/
_emitBlur = (target, payload) => {
_emitWillBlur = (target, payload) => {
if (this._lastWillFocusKey === target) {
this._lastWillFocusKey = undefined;
}
@@ -205,6 +276,7 @@ export default class NavigationEventManager extends React.Component {
return;
}
this._lastDidBlurKey = undefined;
this._lastWillBlurKey = target;
const { navigation, onEvent } = this.props;
@@ -212,10 +284,42 @@ export default class NavigationEventManager extends React.Component {
onEvent(target, 'willBlur', payload);
if (typeof navigation.state.isTransitioning !== 'boolean') {
onEvent(target, 'didBlur', payload);
this._emitDidBlur(target, payload);
}
};
/**
* @param {string} target
* @param {Payload} payload
*/
_emitDidFocus = (target, payload) => {
if (this._lastWillFocusKey !== target || this._lastDidFocusKey === target) {
return;
}
this._lastDidFocusKey = target;
const { onEvent } = this.props;
onEvent(target, 'didFocus', payload);
};
/**
* @param {string} target
* @param {Payload} payload
*/
_emitDidBlur = (target, payload) => {
if (this._lastWillBlurKey !== target || this._lastDidBlurKey === target) {
return;
}
this._lastDidBlurKey = target;
const { onEvent } = this.props;
onEvent(target, 'didBlur', payload);
};
render() {
return null;
}

View File

@@ -308,21 +308,21 @@ export default class StackView extends React.Component<Props, State> {
return <HeaderContainer {...props} />;
};
private handleTransitionComplete = ({ route }: { route: Route<string> }) => {
private handleTransitionComplete = () => {
const { state, navigation } = this.props;
if (state.isTransitioning) {
navigation.dispatch(
StackActions.completeTransition({
key: navigation.state.key,
toChildKey: route.key,
toChildKey: state.routes[state.index].key,
})
);
}
};
private handleOpenRoute = ({ route }: { route: Route<string> }) => {
this.handleTransitionComplete({ route });
this.handleTransitionComplete();
this.setState((state) => ({
routes: state.replacingRouteKeys.length
? state.routes.filter((r) => !state.replacingRouteKeys.includes(r.key))
@@ -347,13 +347,7 @@ export default class StackView extends React.Component<Props, State> {
// @ts-ignore
navigation.dispatch(StackActions.pop({ key: route.key, prune: false }));
} else {
// While closing route we need to point to the previous one assuming that
// this previous one in routes array
const index = this.state.routes.findIndex((r) => r.key === route.key);
this.handleTransitionComplete({
route: this.state.routes[Math.max(index - 1, 0)],
});
this.handleTransitionComplete();
// We need to clean up any state tracking the route and pop it immediately
this.setState((state) => ({