mirror of
https://github.com/zebrajr/react.git
synced 2026-01-15 12:15:22 +00:00
Subscriptions shouldn't call setState after unmount even for Promises (#12425)
This commit is contained in:
@@ -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(<Subscription source={promise}>{render}</Subscription>);
|
||||
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', () => {
|
||||
|
||||
@@ -64,6 +64,7 @@ export function createSubscription<Property, Value>(
|
||||
: undefined,
|
||||
};
|
||||
|
||||
_hasUnmounted: boolean = false;
|
||||
_unsubscribe: Unsubscribe | null = null;
|
||||
|
||||
static getDerivedStateFromProps(nextProps, prevState) {
|
||||
@@ -93,6 +94,10 @@ export function createSubscription<Property, Value>(
|
||||
|
||||
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<Property, Value>(
|
||||
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) {
|
||||
|
||||
Reference in New Issue
Block a user