Thanks to visit codestin.com
Credit goes to github.com

Skip to content
This repository was archived by the owner on Apr 13, 2023. It is now read-only.

Commit 8204605

Browse files
wsmdrosskevin
authored andcommitted
Invoke onCompleted/onError even if Mutiation unmounts (#2710)
* invoke onCompleted/onError after Mutiation unmounts * Add tests for when Mutation is unmounted
1 parent 65e734e commit 8204605

File tree

2 files changed

+186
-89
lines changed

2 files changed

+186
-89
lines changed

src/Mutation.tsx

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -242,9 +242,6 @@ class Mutation<TData = any, TVariables = OperationVariables> extends React.Compo
242242
};
243243

244244
private onMutationCompleted = (response: ExecutionResult<TData>, mutationId: number) => {
245-
if (this.hasMounted === false) {
246-
return;
247-
}
248245
const { onCompleted, ignoreResults } = this.props;
249246

250247
const { data, errors } = response;
@@ -253,21 +250,18 @@ class Mutation<TData = any, TVariables = OperationVariables> extends React.Compo
253250

254251
const callOncomplete = () => (onCompleted ? onCompleted(data as TData) : null);
255252

256-
if (this.isMostRecentMutation(mutationId) && !ignoreResults) {
253+
if (this.hasMounted && this.isMostRecentMutation(mutationId) && !ignoreResults) {
257254
this.setState({ loading: false, data, error }, callOncomplete);
258255
} else {
259256
callOncomplete();
260257
}
261258
};
262259

263260
private onMutationError = (error: ApolloError, mutationId: number) => {
264-
if (this.hasMounted === false) {
265-
return;
266-
}
267261
const { onError } = this.props;
268262
const callOnError = () => (onError ? onError(error) : null);
269263

270-
if (this.isMostRecentMutation(mutationId)) {
264+
if (this.hasMounted && this.isMostRecentMutation(mutationId)) {
271265
this.setState({ loading: false, error }, callOnError);
272266
} else {
273267
callOnError();

test/client/Mutation.test.tsx

Lines changed: 184 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1476,107 +1476,210 @@ it('errors when changing from mutation to a subscription', done => {
14761476
console.log = errorLogger; // tslint:disable-line
14771477
});
14781478

1479-
it('does not update state after receiving data after it has been unmounted', done => {
1480-
let success = false;
1481-
let original = console.error;
1482-
console.error = jest.fn();
1483-
const checker = () => {
1484-
setTimeout(() => {
1485-
expect(console.error).not.toHaveBeenCalled();
1486-
console.error = original;
1487-
success = true;
1488-
done();
1489-
}, 100);
1490-
};
1491-
1492-
class Component extends React.Component {
1493-
state = {
1494-
called: false,
1479+
describe('after it has been unmounted', () => {
1480+
it('calls the onCompleted prop after the mutation is complete', done => {
1481+
let success = false;
1482+
const onCompletedFn = jest.fn();
1483+
const checker = () => {
1484+
setTimeout(() => {
1485+
success = true;
1486+
expect(onCompletedFn).toHaveBeenCalledWith(data);
1487+
done();
1488+
}, 100);
14951489
};
14961490

1497-
render() {
1498-
const { called } = this.state;
1499-
if (called === true) {
1500-
return null;
1501-
} else {
1502-
return (
1503-
<Mutation mutation={mutation}>
1504-
{(createTodo, result) => {
1505-
if (!result.called) {
1491+
class Component extends React.Component {
1492+
state = {
1493+
called: false,
1494+
};
1495+
1496+
render() {
1497+
const { called } = this.state;
1498+
if (called === true) {
1499+
return null;
1500+
} else {
1501+
return (
1502+
<Mutation mutation={mutation} onCompleted={onCompletedFn}>
1503+
{createTodo => {
15061504
setTimeout(() => {
15071505
createTodo();
15081506
this.setState({ called: true }, checker);
15091507
});
1510-
}
1508+
return null;
1509+
}}
1510+
</Mutation>
1511+
);
1512+
}
1513+
}
1514+
}
15111515

1512-
return null;
1513-
}}
1514-
</Mutation>
1516+
mount(
1517+
<MockedProvider mocks={mocks}>
1518+
<Component />
1519+
</MockedProvider>,
1520+
);
1521+
1522+
setTimeout(() => {
1523+
if (!success) done.fail('timeout passed');
1524+
}, 200);
1525+
});
1526+
1527+
it('calls the onError prop if the mutation encounters an error', done => {
1528+
let success = false;
1529+
const onErrorFn = jest.fn();
1530+
const checker = () => {
1531+
setTimeout(() => {
1532+
success = true;
1533+
expect(onErrorFn).toHaveBeenCalledWith(
1534+
expect.objectContaining({ message: 'Network error: error occurred' }),
15151535
);
1536+
done();
1537+
}, 100);
1538+
};
1539+
1540+
class Component extends React.Component {
1541+
state = {
1542+
called: false,
1543+
};
1544+
1545+
render() {
1546+
const { called } = this.state;
1547+
if (called === true) {
1548+
return null;
1549+
} else {
1550+
return (
1551+
<Mutation mutation={mutation} onError={onErrorFn}>
1552+
{createTodo => {
1553+
setTimeout(() => {
1554+
createTodo();
1555+
this.setState({ called: true }, checker);
1556+
});
1557+
return null;
1558+
}}
1559+
</Mutation>
1560+
);
1561+
}
15161562
}
15171563
}
1518-
}
15191564

1520-
mount(
1521-
<MockedProvider mocks={mocks}>
1522-
<Component />
1523-
</MockedProvider>,
1524-
);
1565+
const mockError = [
1566+
{
1567+
request: { query: mutation },
1568+
error: new Error('error occurred'),
1569+
},
1570+
];
15251571

1526-
setTimeout(() => {
1527-
if (!success) done.fail('timeout passed');
1528-
}, 200);
1529-
});
1572+
mount(
1573+
<MockedProvider mocks={mockError}>
1574+
<Component />
1575+
</MockedProvider>,
1576+
);
1577+
});
1578+
1579+
it('does not update state after receiving data', done => {
1580+
let success = false;
1581+
let original = console.error;
1582+
console.error = jest.fn();
1583+
const checker = () => {
1584+
setTimeout(() => {
1585+
expect(console.error).not.toHaveBeenCalled();
1586+
console.error = original;
1587+
success = true;
1588+
done();
1589+
}, 100);
1590+
};
1591+
1592+
class Component extends React.Component {
1593+
state = {
1594+
called: false,
1595+
};
1596+
1597+
render() {
1598+
const { called } = this.state;
1599+
if (called === true) {
1600+
return null;
1601+
} else {
1602+
return (
1603+
<Mutation mutation={mutation}>
1604+
{(createTodo, result) => {
1605+
if (!result.called) {
1606+
setTimeout(() => {
1607+
createTodo();
1608+
this.setState({ called: true }, checker);
1609+
});
1610+
}
1611+
1612+
return null;
1613+
}}
1614+
</Mutation>
1615+
);
1616+
}
1617+
}
1618+
}
1619+
1620+
mount(
1621+
<MockedProvider mocks={mocks}>
1622+
<Component />
1623+
</MockedProvider>,
1624+
);
15301625

1531-
it('does not update state after receiving error after it has been unmounted', done => {
1532-
let original = console.error;
1533-
console.error = jest.fn();
1534-
const checker = () => {
15351626
setTimeout(() => {
1536-
expect(console.error).not.toHaveBeenCalled();
1537-
console.error = original;
1538-
done();
1539-
}, 100);
1540-
};
1627+
if (!success) done.fail('timeout passed');
1628+
}, 200);
1629+
});
15411630

1542-
class Component extends React.Component {
1543-
state = {
1544-
called: false,
1631+
it('does not update state after receiving error', done => {
1632+
const onError = jest.fn();
1633+
let original = console.error;
1634+
console.error = jest.fn();
1635+
const checker = () => {
1636+
setTimeout(() => {
1637+
expect(onError).toHaveBeenCalled();
1638+
expect(console.error).not.toHaveBeenCalled();
1639+
console.error = original;
1640+
done();
1641+
}, 100);
15451642
};
15461643

1547-
render() {
1548-
const { called } = this.state;
1549-
if (called === true) {
1550-
return null;
1551-
} else {
1552-
return (
1553-
<Mutation mutation={mutation}>
1554-
{(createTodo, result) => {
1555-
if (!result.called) {
1556-
setTimeout(() => {
1557-
createTodo().catch(() => {}); // tslint:disable-line
1558-
this.setState({ called: true }, checker);
1559-
}, 10);
1560-
}
1644+
class Component extends React.Component {
1645+
state = {
1646+
called: false,
1647+
};
1648+
1649+
render() {
1650+
const { called } = this.state;
1651+
if (called === true) {
1652+
return null;
1653+
} else {
1654+
return (
1655+
<Mutation mutation={mutation} onError={onError}>
1656+
{(createTodo, result) => {
1657+
if (!result.called) {
1658+
setTimeout(() => {
1659+
createTodo().catch(() => {}); // tslint:disable-line
1660+
this.setState({ called: true }, checker);
1661+
}, 10);
1662+
}
15611663

1562-
return null;
1563-
}}
1564-
</Mutation>
1565-
);
1664+
return null;
1665+
}}
1666+
</Mutation>
1667+
);
1668+
}
15661669
}
15671670
}
1568-
}
15691671

1570-
const mockError = [
1571-
{
1572-
request: { query: mutation },
1573-
error: new Error('error occurred'),
1574-
},
1575-
];
1672+
const mockError = [
1673+
{
1674+
request: { query: mutation },
1675+
error: new Error('error occurred'),
1676+
},
1677+
];
15761678

1577-
const wrapper = mount(
1578-
<MockedProvider mocks={mockError}>
1579-
<Component />
1580-
</MockedProvider>,
1581-
);
1679+
const wrapper = mount(
1680+
<MockedProvider mocks={mockError}>
1681+
<Component />
1682+
</MockedProvider>,
1683+
);
1684+
});
15821685
});

0 commit comments

Comments
 (0)