diff --git a/packages/create-subscription/src/__tests__/createSubscription-test.internal.js b/packages/create-subscription/src/__tests__/createSubscription-test.internal.js
index 8dee9bfa5c..2cc81b696d 100644
--- a/packages/create-subscription/src/__tests__/createSubscription-test.internal.js
+++ b/packages/create-subscription/src/__tests__/createSubscription-test.internal.js
@@ -189,6 +189,38 @@ describe('createSubscription', () => {
// Ensure that only Promise B causes an update
expect(ReactNoop.flush()).toEqual([123]);
});
+
+ it('should not call setState for a Promise that resolves after unmount', async () => {
+ const Subscription = createSubscription({
+ getCurrentValue: source => undefined,
+ subscribe: (source, callback) => {
+ source.then(value => callback(value), value => callback(value));
+ // (Can't unsubscribe from a Promise)
+ return () => {};
+ },
+ });
+
+ function render(hasLoaded) {
+ ReactNoop.yield('rendered');
+ return null;
+ }
+
+ let resolvePromise;
+ const promise = new Promise((resolve, reject) => {
+ resolvePromise = resolve;
+ });
+
+ ReactNoop.render({render});
+ expect(ReactNoop.flush()).toEqual(['rendered']);
+
+ // Unmount
+ ReactNoop.render(null);
+ ReactNoop.flush();
+
+ // Resolve Promise should not trigger a setState warning
+ resolvePromise(true);
+ await promise;
+ });
});
it('should unsubscribe from old subscribables and subscribe to new subscribables when props change', () => {
diff --git a/packages/create-subscription/src/createSubscription.js b/packages/create-subscription/src/createSubscription.js
index 699db33781..df18f8b3c7 100644
--- a/packages/create-subscription/src/createSubscription.js
+++ b/packages/create-subscription/src/createSubscription.js
@@ -64,6 +64,7 @@ export function createSubscription(
: undefined,
};
+ _hasUnmounted: boolean = false;
_unsubscribe: Unsubscribe | null = null;
static getDerivedStateFromProps(nextProps, prevState) {
@@ -93,6 +94,10 @@ export function createSubscription(
componentWillUnmount() {
this.unsubscribe(this.state);
+
+ // Track mounted to avoid calling setState after unmounting
+ // For source like Promises that can't be unsubscribed from.
+ this._hasUnmounted = true;
}
render() {
@@ -103,6 +108,10 @@ export function createSubscription(
const {source} = this.state;
if (source != null) {
const callback = (value: Value | void) => {
+ if (this._hasUnmounted) {
+ return;
+ }
+
this.setState(state => {
// If the value is the same, skip the unnecessary state update.
if (value === state.value) {