5 ms·
The problem with resolving promises inside of `componentDidMount` is that there is no way to cancel those promises. If the component is unmounted, and then setS
by jbaudanza 9y ago
The problem with resolving promises inside of `componentDidMount` is that there is no way to cancel those promises. If the component is unmounted, and then setState is called, it will generate an error (or a warning. I can't remember).
Facebook discusses this on its blog: https://reactjs.org/blog/2015/12/16/ismounted-antipattern.html https://reactjs.org/blog/2015/12/16/ismounted-antipattern.ht...
I've been using Observables which, in my experience, bind a little cleaner than Promises to React components.
- Guillaume86 9y agoYes there was a whole discussion in a github issue about the correct pattern to solve this. Creating and guarding against a `isMounted` field in your `.then()` callbacks means you prevent garbage collection of the unmounted components. An easy solution is to use cancellable promises (with some subtleties to allow garbage collection). There's an implementation which take care of this properly here (provides an HoC): https://github.com/hjylewis/trashable-react https://github.com/hjylewis/trashable-react .
- loevborg 9y agoWhen you say "cancellable promises", that means that you cannot use native JS promises, correct?
- Guillaume86 9y agoYou can, you just need to wrap it when used inside a react component.
- drdaeman 9y agoHow? I've thought it's just technically impossible (no API to do so) to cancel a `fetch` request in progress.
- myhf 9y agoYou don't have to cancel the fetch request, you just have to cancel its callback.
- Guillaume86 9y agoWe're talking about several different things: Native promises do not have a "real" cancellation API (no 3rd path besides resolved and rejected) but you can mimic the functionality by rejecting with a dedicated error type, or use another promise as a cancellation token for example. fetch(), which uses promises as return type, do not support aborting requests, as discussed [here](https://github.com/whatwg/fetch/issues/447 https://github.com/whatwg/fetch/issues/447) but the [old XMLHttpRequest does](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/abort https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequ...). Aborting is specific to HTTP requests, it's stopping the connection to the remote server (so technically impossible to implement with the current APIs as you said). Cancelling a promise is just stopping all onResolved continuations to be called, so it can be implemented in several different ways as I explained above. The end result if you use a classic cancellation wrapper on a fetch() promise is that the onReject continuations will be called immediately with a specific reason. The HTTP request will continue to the end (no abort), but the response will be ignored. In the case of the react library I mentioned, both the onResolved and onReject continuations will be ignored if cancelled, it's the subtlety that allows the component to be garbage collected (and probably why the author decided to call that "thrashable" as opposed to "cancellable" to mark the difference).
- always_good 9y agoFor example, you can wrap a promise such that when it finally resolves, it checks an isCancelled flag and rejects instead so that your .then callbacks aren't run. It's not about cancelling any potential inflight action within the promise itself. In this case, you don't want the callback to run after the react component unmounts, nor do you want to hold a reference to the component: promise .then(value => this.setState({ value }))
- rounce 9y agoPromises? Cancellation? The whole point of a Promise is that it must resolve to something eventually.
- ryanbrunner 9y agoYou're cancelling the promise from the perspective of the caller, not the Promise itself. Promises themselves can't decide that they don't want to resolve, but there's plenty of cases where whoever requested the promise in the first place is no longer interested in the results.
- k__ 9y agoit would be nice if React.Component had a cancel method, so you could simply drop all your promises into it and they get canceled when the component unmounts. this.cancelOnUnmoun(new Promise(...)).then(...)
- parmesan 9y agoIn Android (for example), one would use an observable stream bound to the lifecycle of the host. Most folks do this using Rx and add a custom operator for unsubscribing on the host DESTROY event.
- FLUX-YOU 9y agoWhere is the typical place to put initial fetches? I've been setting empty state and then calling a method at the end of a constructor which ultimately results in a promise that calls setState with the data. But I would never do something like that in, say, the constructor of a WPF/UWP windows app as those have "Loaded" or "Loading" events of some kind (which is parallel to componentDidMount).
- ihsw2 9y ago`componentDidMount`, which is after a React component has been rendered. This contradicts the parent commenter directly but I'm fine with that. The React lifecycle has `componentDidMount` for doing stuff (at most once) after rendering.
- always_good 9y agoI don't understand what you're saying. Once the component mounts, then it can be unmounted. The upstream comment is talking about when you have a promise that is still running after unmount.
- pcmaffey 9y agoProtect the setStates in the callback with a conditional switch that gets flipped off in componentWillUnmount.
- whistlerbrk 9y agoNot clear on what you're saying, pseudocode?
- topgunsarg 9y agoIn componentWillUnmount, set a var componentUnmounted true. In componentDidMount in the callback for the fetch, check for componentUnmounted before setting state for an error or whatever you want to do.
- travmatt 9y agoSet a variable... set a property in the component state? Or set a static variable on the class?
- pcmaffey 9y agoI use a static var on the class. componentDidMount() { if (!this.unmounted) ... } componentWillUnmount() { this.unmounted = true }
- fatso83 9y agoThat's not a static car on the class. It's on the instance! A static class property would entail something like `Foo.unmounted = true` (a bad idea).