fix: NavigationEvents subscribe events on new nav state (#8920)

If the navigation prop changes, the NavigationEvents component was not
subscribing to the new value. These changes fix this problem and add a
test to verify that behavior.
This commit is contained in:
oltrep
2020-10-01 07:00:49 -07:00
committed by GitHub
parent 20e2625f35
commit 6390aacd07
2 changed files with 60 additions and 5 deletions

View File

@@ -12,11 +12,28 @@ const EventNames = Object.keys(EventNameToPropName);
class NavigationEvents extends React.Component {
componentDidMount() {
this.subscriptions = {};
// We register all navigation listeners on mount to ensure listener stability across re-render
// A former implementation was replacing (removing/adding) listeners on all update (if prop provided)
// but there were issues (see https://github.com/react-navigation/react-navigation/issues/5058)
this.subscribeAll();
}
componentDidUpdate(prevProps) {
if (this.props.navigation !== prevProps.navigation) {
this.removeAll();
this.subscribeAll();
}
}
componentWillUnmount() {
this.removeAll();
}
getPropListener = (eventName) => this.props[EventNameToPropName[eventName]];
subscribeAll() {
this.subscriptions = {};
EventNames.forEach((eventName) => {
this.subscriptions[eventName] = this.props.navigation.addListener(
eventName,
@@ -28,14 +45,12 @@ class NavigationEvents extends React.Component {
});
}
componentWillUnmount() {
removeAll() {
EventNames.forEach((eventName) => {
this.subscriptions[eventName].remove();
});
}
getPropListener = (eventName) => this.props[EventNameToPropName[eventName]];
render() {
return null;
}

View File

@@ -5,6 +5,13 @@ import NavigationContext from '../NavigationContext';
const createPropListener = () => jest.fn();
const EVENT_TO_PROP_NAME = {
willFocus: 'onWillFocus',
didFocus: 'onDidFocus',
willBlur: 'onWillBlur',
didBlur: 'onDidBlur',
};
// An easy way to create the 4 listeners prop
const createEventListenersProp = () => ({
onWillFocus: createPropListener(),
@@ -122,6 +129,39 @@ describe('NavigationEvents', () => {
checkPropListenerIsCalled('didBlur', 'onDidBlur');
});
it('wires props listeners to latest navigation updates', () => {
const {
navigation,
NavigationListenersAPI,
} = createTestNavigationAndHelpers();
const {
navigation: nextNavigation,
NavigationListenersAPI: nextNavigationListenersAPI,
} = createTestNavigationAndHelpers();
const eventListenerProps = createEventListenersProp();
const component = renderer.create(
<NavigationEvents navigation={navigation} {...eventListenerProps} />
);
Object.entries(EVENT_TO_PROP_NAME).forEach(([eventName, propName]) => {
expect(eventListenerProps[propName]).toHaveBeenCalledTimes(0);
NavigationListenersAPI.call(eventName);
expect(eventListenerProps[propName]).toHaveBeenCalledTimes(1);
});
component.update(
<NavigationEvents navigation={nextNavigation} {...eventListenerProps} />
);
Object.entries(EVENT_TO_PROP_NAME).forEach(([eventName, propName]) => {
NavigationListenersAPI.call(eventName);
expect(eventListenerProps[propName]).toHaveBeenCalledTimes(1);
nextNavigationListenersAPI.call(eventName);
expect(eventListenerProps[propName]).toHaveBeenCalledTimes(2);
});
});
it('wire latest props listener to navigation listeners on updates (support closure/arrow functions update)', () => {
const {
navigation,