Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cancel All Subscriptions and Asyncs in the componentWillUnmount Method, how?

I'm getting an error message due to an async method issue. In my terminal I'm seeing:

Warning: Can't call setState (or forceUpdate) on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method. - node_modules/fbjs/lib/warning.js:33:20 in printWarning - node_modules/fbjs/lib/warning.js:57:25 in warning - node_modules/react-native/Libraries/Renderer/ReactNativeRenderer-dev.js:12196:6 in warnAboutUpdateOnUnmounted - node_modules/react-native/Libraries/Renderer/ReactNativeRenderer-dev.js:13273:41 in scheduleWorkImpl - node_modules/react-native/Libraries/Renderer/ReactNativeRenderer-dev.js:6224:19 in enqueueSetState - node_modules/react/cjs/react.development.js:242:31 in setState * router/_components/item.js:51:16 in getImage$ - node_modules/regenerator-runtime/runtime.js:62:44 in tryCatch - node_modules/regenerator-runtime/runtime.js:296:30 in invoke - ... 13 more stack frames from framework internals 

I noticed it's specifically pointing out the getImage$

Here's the code I'm using for that section:

export default class extends Component {     constructor(props) {         super(props);         const { item } = props          const bindThese = { item }         this.boundActionCreators = bindActionCreators(bindThese)          this.state = {             image: require('../../static/logo.png'),             ready: false,             showOptions: this.props.showOptions         }          this.getImage = this.getImage.bind(this)         this.renderNotAdmin = this.renderNotAdmin.bind(this)         this.renderAdmin = this.renderAdmin.bind(this)         this.handleOutOfStock = this.handleOutOfStock.bind(this)     }      async getImage(img) {         let imgUri = await Amplify.Storage.get(img)         let uri = await CacheManager.get(imgUri).getPath()          this.setState({             image: { uri },             ready: true         })     }      componentDidMount() {         this.getImage(this.props.item.image)     } 

I'm trying to figure out how to use a componentWillUnmount with this async method. How do I go about it?

Thanks!

like image 789
Dres Avatar asked Aug 28 '18 15:08

Dres


People also ask

How do you cancel async task in React?

Also, in order to cancel an active fetch request, you need to use an AbortController instance. Try the demo. let controller = new AbortController() creates an instance of the abort controller. Then await fetch(..., { signal: controller.

What do you do in componentWillUnmount?

componentWillUnmount is the last function to be called immediately before the component is removed from the DOM. It is generally used to perform clean-up for any DOM-elements or timers created in componentWillMount . At a picnic, componentWillUnmount corresponds to just before you pick up your picnic blanket.

Can't perform a React state update on an unmounted component This is a no op but it indicates a memory leak in your application to fix cancel all subs?

Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.

Is component mounted React?

Almost everything follows this cycle in its life, and React components do as well. Components are created (mounted on the DOM), grow by updating, and then die (unmount on DOM).


2 Answers

You can use isMounted React pattern to avoid memory leaks here.

In your constructor:

constructor(props) {     super(props);      this._isMounted = false; // rest of your code }  componentDidMount() {     this._isMounted = true;     this._isMounted && this.getImage(this.props.item.image); 

}

in your componentWillUnmount

componentWillUnmount() {    this._isMounted = false; } 

While in you getImage()

async getImage(img) {     let imgUri = await Amplify.Storage.get(img)     let uri = await CacheManager.get(imgUri).getPath()      this._isMounted && this.setState({         image: { uri },         ready: true     }) } 

A recommend approach to use Axios which is based cancellable promise pattern. So you can cancel any network call while unmounting the component with it's cancelToken subscription. Here is resource for Axios Cancellation

like image 127
Sakhi Mansoor Avatar answered Sep 22 '22 15:09

Sakhi Mansoor


From the React blog

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.

It goes on to say that ideally, this would instead be fixed by using cancellable callbacks, although the first solution seems suitable here.

What you definitely shouldn't do is use the isMounted() function, which may be deprecated.

like image 24
Keir Avatar answered Sep 21 '22 15:09

Keir