@samitouri / QOS-React-2 / commits / bec238a7c9

Added post about upgrading your code to avoid isMounted()

Jim committed Dec 2, 2015 at 18:07 UTC bec238a7c9c1fbd89abbcf57ffbcb3fc95ade829
2 files changed +79 -2
docs/_posts/2015-12-16-ismounted-antipattern.md new
+77
@@ -0,0 +1,77 @@
1 +---
2 +title: "isMounted is an Antipattern"
3 +author: jimfb
4 +---
5 +
6 +As we move closer to officially deprecating isMounted, it's worth understanding why the function is an antipattern, and how to write code without the isMounted function.
7 +
8 +The primary use case for `isMounted()` is to avoid calling `setState()` after a component has unmounted, because calling `setState()` after a component has unmounted will emit a warning. The “setState warning” exists to help you catch bugs, because calling `setState()` on an unmounted component is an indication that your app/component has somehow failed to clean up properly. Specifically, calling `setState()` in an unmounted component means that your app is still holding a reference to the component after the component has been unmounted - which often indicates a memory leak!
9 +
10 +To avoid the error message, people often add lines like this:
11 +
12 +```js
13 +if(this.isMounted()) { // This is bad.
14 + this.setState({...});
15 +}
16 +```
17 +
18 +Checking `isMounted` before calling `setState()` does eliminate the warning, but it also defeats the purpose of the warning, since now you will never get the warning (even when you should!)
19 +
20 +Other uses of `isMounted()` are similarly erroneous; using `isMounted()` is a code smell because the only reason you would check is because you think you might be holding a reference after the component has unmounted.
21 +
22 +An easy migration strategy for anyone upgrading their code to avoid `isMounted()` is to track the mounted status yourself. Just set a `_isMounted` property to true in `componentDidMount` and set it to false in `componentWillUnmount`, and use this variable to check your component's status.
23 +
24 +An optimal solution would be to find places where `setState()` might be called after a component has unmounted, and fix them. Such situations most commonly occur due to callbacks, when a component is waiting for some data and gets unmounted before the data arrives. Ideally, any callbacks should be canceled in `componentWillUnmount`, prior to unmounting.
25 +
26 +For instance, if you are using a Flux store in your component, you must unsubscribe in `componentWillUnmount`:
27 +
28 +```javascript{9}
29 +class MyComponent extends React.Component {
30 + componentDidMount() {
31 + mydatastore.subscribe(this);
32 + }
33 + render() {
34 + ...
35 + }
36 + componentWillUnmount() {
37 + mydatastore.unsubscribe(this);
38 + }
39 +}
40 +```
41 +
42 +If you use ES6 promises, you may need to wrap your promise in order to make it cancelable.
43 +
44 +```js
45 +const cancelablePromise = makeCancelable(
46 + new Promise(r => component.setState({...}}))
47 +);
48 +
49 +cancelablePromise
50 + .promise
51 + .then(() => console.log('resolved'))
52 + .catch((reason) => console.log('isCanceled', reason.isCanceled));
53 +
54 +cancelablePromise.cancel(); // Cancel the promise
55 +```
56 +
57 +Where `makeCancelable` is [defined by @istarkov](https://github.com/facebook/react/issues/5465#issuecomment-157888325) as:
58 +
59 +```js
60 +const makeCancelable = (promise) => {
61 + let hasCanceled_ = false;
62 +
63 + return {
64 + promise: new Promise(
65 + (resolve, reject) => promise
66 + .then(r => hasCanceled_
67 + ? reject({isCanceled: true})
68 + : resolve(r)
69 + )
70 + ),
71 + cancel() {
72 + hasCanceled_ = true;
73 + },
74 + };
75 +};
76 +```
77 +As an added bonus for getting your code cleaned up early, getting rid of `isMounted()` makes it one step easier for you to upgrade to ES6 classes, where using `isMounted()` is already prohibited. Happy coding!
docs/docs/ref-02-component-api.md
+2 -2
@@ -106,7 +106,7 @@ boolean isMounted()
106
107 > Note:
108 >
109 -> This method is not available on ES6 `class` components that extend `React.Component`. It may be removed entirely in a future version of React.
109 +> This method is not available on ES6 `class` components that extend `React.Component`. It will likely be removed entirely in a future version of React, so you might as well [start migrating away from isMounted() now](/react/blog/2015/12/16/ismounted-antipattern.html).
110
111
112 ### setProps
@@ -143,4 +143,4 @@ Like `setProps()` but deletes any pre-existing props instead of merging the two
143
144 > Note:
145 >
146 -> This method is deprecated and will be removed soon. This method is not available on ES6 `class` components that extend `React.Component`. Instead of calling `replaceProps`, try invoking ReactDOM.render() again with the new props. For additional notes, see our [blog post about using the Top Level API](/react/blog/2015/10/01/react-render-and-top-level-api.html)
\ No newline at end of file
146 +> This method is deprecated and will be removed soon. This method is not available on ES6 `class` components that extend `React.Component`. Instead of calling `replaceProps`, try invoking ReactDOM.render() again with the new props. For additional notes, see our [blog post about using the Top Level API](/react/blog/2015/10/01/react-render-and-top-level-api.html)