Is it necessary to unmount state within ComponentWillUnmount? - javascript

I'm performing server requests in my app within componentDidMount.So I'm calling setState within componentDidMount.Do I need to unmount this state within componentWillUnmount ?Is this is a solution for avoiding memory leaks in my app ?Please help me to find a solution for this.Thank you!
sample code
componentDidMount(){
fetch({ /* ... */ })
.then(res => res.json())
.then((responseData) => {
this.setState({
result: responseData.meta.data
})
})
}
componentWillUnmount(){
this.setState({
result:''
})
}

It's not needed to unmount the state. Setting result to empty string isn't any better than setting it to any other value.
The cause of memory leaks is that a reference to an object (component instance) is used somewhere, this prevents it from being garbage-collected as unused.
In this piece of code setState can be called after the component is unmounted because the request isn't cancelled. This will cause a warning:
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 the componentWillUnmount method.
In case a request is long enough, this will cause a memory leak. In order to avoid it, a request or a promise that causes setState call needs to be cancelled. As for Fetch API request, can be done with AbortController.

React cleans the state of the component upon unmounting, so you don't have to reset the state on componentWillUnmount.
Something that can happen with performing request in components, is that your request can be completed after your component is unmounted again. At that point you're trying to perform setState on a component that isn't mounted. You'll receive an error in your console at that point.
Most HTTP libraries offer functionalities to cancel requests, this can be used to prevent that from happening. An example from the axios library here...

You wouldnt have to worry about cleaning up state while unmounting, as the reasons are already being highlighted by the people above.
But if you have left the listeners attached to your component "unremoved", then that might be a possible candidate for memory leak.
componentDidMount() can be used as a place to attach listeners and componentWillUnmount to remove those attached listeners.'
In Flux architecture, we use them to attach and remove listeners for events emitted from stores.
public componentDidMount() {
sampleStore.addListener('sampleEventFired', this.oncatch);
}
public componentWillUnmount() {
sampleStore.removeListener('sampleEventFired', this.oncatch);
}

Related

How is this simple useEffect() causing a memory leak?

I only use useEffect() in one place in my app as follows:
useEffect(() => {
document.body.addEventListener('click', bodyClicked);
return () => {
document.body.removeEventListener('click', bodyClicked);
};
}, [bodyClicked]);
function bodyClicked() {
dispatch({type: 'toggleMenuPageOff'});
}
I am getting this warning:
react_devtools_backend.js:4061 Warning: 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.
at WEBPACK_DEFAULT_EXPORT (http://localhost:3000/bundle.js:4146:70)
at div
Why is this a memory leak and how can I write it correctly to remove the warning?
You're creating an infinite loop. the function you're if inside the components code is being recreated on each rerender which means it will trigger useEffect and therefore be called again and again...
https://reactjs.org/docs/hooks-reference.html#usecallback
useEffect is regsitered with DOM and when the component is done running, then it is done running, your component is no longer in dom but browser is still listening to click event or in other words you are trying to perform click action after the component is unmounted this leads to memory leaks. Anything inside is useEffect is sideeffect and you gotta clean up the code to remove potential consequences
just inside of useeffect() add clean up code which is again a function that has return response of your useEffect() that actually clean up your code sideeffects
in your code you are using body clciked in your dependancies array keep that to [] empty
useEffect(()=> {
function bodyClicked(){
window.bodyclicked(.....)}
return function(){
window.removeeventlistener("click",bodyClicked)}
}},[])

Cancel graphQL call on component unmount

I am using below #apollo/GraphQL query inside a React native component
const [getContent, {
error, data, fetchMore, called
}] = useLazyQuery(BROWSE_CONTENT, {
variables: {
sessionIdToken: sessionId,
deviceLocale,
paginationCursor: `'${payload.endCursor}'`
}
})
Once i have data, I use RecyclerListView to render the list.
Problem: since its async call (useLazyQuery), so if user quickly change the View (by clicking the button) i am getting below error.
Warning: 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 the componentWillUnmount method.
in RecyclerListView (at MyComponent.js:345)
How to cancel useLazyQuery on component unmount.

How should I alternate componentWillMount()?

I'm using React.js, and as you know, componentWillMount() is going to be deprecated.
I wanna replace my componentWillMounts.
I'm going to move its logics into constructor. Is there any difference between executing some logic in componentWillMount and in constructor?
For example,
before
class Hello extends React.Component {
componentWillMount() {
doSomething();
}
render() {
return <div>{this.state.name} </div>
}
}
after
class Hello extends React.Component {
constructor(props) {
super(props);
doSomething();
}
render() {
return <div>{this.state.name} </div>
}
}
Also, when doSomething is setState, is there any difference setting state in constructor and in public prop?
in constructor
constructor(props) {
super(props);
this.state = { foo: 1 };
}
in public prop
state = { foo: 1 };
constructor is not the right place for performing some actions. Because it will hold other operations until it's finished.
componentDidMount is the right choice because it's an asynchronous function so that actions are run in the background and there'll be no hamper in the UI rendering.
Here's a list that you can choose when to use between constructor and componentDidMount:
constructor
Do:
initialize state
bind event handlers
If you don't initialize a state and you don't bind methods, you don't need to implement the constructor.
Don't:
Avoid introducing any side-effects or subscriptions. Do not set state by using setState() in the constructor.
componentDidMount
Do:
initialization that requires DOM nodes
load data from a remote endpoint (where to instantiate the network request)
set up any subscriptions (don’t forget to unsubscribe in componentWillUnmount())
You may also be interested to read the comment from the creator of react, Dan Abramov:
I won't like to wait for the component to be mounted to dispatch an ajax call to fulfill the component data dependencies. I would like to do it as soon as possible, like in the constructor, not even in componentWillMount.
If it's an async request, it won't be fulfilled by the time the component mounts anyway, regardless of where you fire it. This is because JS is single threaded, and the network request can't "come back" and be handled while we are still rendering. So the difference between firing it earlier and later is often negligible.
You're right that it matters in some rare cases though and for those cases it might make sense to break the recommendation. But you should be extra cautious as state can update before mounting, and if your data depends on state, you might have to refetch in that case. In other words: when in doubt, do it in componentDidMount.
The specific recommendation to avoid side effects in the constructor and Will* lifecycles is related to the changes we are making to allow rendering to be asynchronous and interruptible (in part to support use cases like this better). We are still figuring out the exact semantics of how it should work, so at the moment our recommendations are more conservative. As we use async rendering more in production we will provide a more specific guidance as to where to fire the requests without sacrificing either efficiency or correctness. But for now providing a clear migration path to async rendering (and thus being more conservative in our recommendations) is more important.
For further interest, you may also visit this post.
getDerivedStateFromProps is invoked right before calling the render method, both on the initial mount and on subsequent updates. It should return an object to update the state, or null to update nothing.
So If you want to perform some action just once, on before mount of component, then getDerivedStateFromProps is not appropriate option.
Use componentDidMount method.
Have more details in https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html
Use getDerivedStateFromProps() it's next method called after constructor.
https://reactjs.org/docs/react-component.html#static-getderivedstatefromprops
setState in a constructor will perform unnecessary computation, you can either use the state property as you suggest or this.state = { ... } in your constructor if you need to perform more computation or access props.
The React documentation recommends you use constructor (or a class property) over componentWillMount for initialising state. For side-effects (e.g. http request) that could update state you should consider componentDidMount or componentDidUpdate. With asynchronous state updates, you should always make sure that your component handles the state without that data.

Memory leak in my React app because of firebase and how to avoid it?

The app I'm working on displays a user dashboard on login with a sidebar for navigation. It uses firebase. I do most of my data fetch from firebase in my async componentDidMount() and store the data in my component state. It takes a couple of seconds to finish all fetches. But if the user decides to navigate to another screen before the fetch is complete, I get the
Can't call setState on unmounted component
warning (as expected). So I do some digging and find that
if(this.isMounted()) this.setState({updates:updates})
makes the warning go away, but then I also find that using isMounted is an antipattern.
The official documentation on this issue suggests tracking the mounted state ourselves by setting _isMounted=true in componentDidMount and then set it to false in the componentWillUnmount. The only way I see to achieve this would be through a variable in component state. Turns out, setState doesn't work in componentWillUnmount. [Issue 1] (I tried calling an external function from componentWillUnmount which in turn sets the state variable. Didn't work.)
The documentation suggests another way, to use cancellable promises. But I'm clueless about how to achieve that with await firebase calls. I also couldn't find any way to stop firebase calls mid-track. [Issue 2]
So now I'm stuck with the warning and data leaks.
a. How do I resolve this problem?
b. Is this something I need to take seriously?
It's good practice to check if the component is still mounted when a request completes, if there is a risk of the component unmounting.
You don't need to put _isMounted in your component state since it will not be used for rendering. You can put it directly on the component instance instead.
Example
class MyComponent extends React.Component {
state = { data: [] };
componentDidMount() {
this._isMounted = true;
fetch("/example")
.then(res => res.json())
.then(res => {
if (this._isMounted) {
this.setState({ data: res.data });
}
});
}
componentWillUnmount() {
this._isMounted = false;
}
render() {
// ...
}
}

Understanding Meteor subscription

I don't understand this example from react-meteor-data
import { createContainer } from 'meteor/react-meteor-data';
export default FooContainer = createContainer(() => {
// Do all your reactive data access in this method.
// Note that this subscription will get cleaned up when your component is unmounted
var handle = Meteor.subscribe("todoList", this.props.id);
return {
currentUser: Meteor.user(),
listLoading: ! handle.ready(),
tasks: Tasks.find({listId: this.props.id}).fetch(),
};
}, Foo);
Why is it recommended to stop subscriptions when a Component is umounted but, in this case, no effort is made to stop anything? How does Meteor handle subscriptions, then? When are the collections cleaned? Are subscriptions stacking up every time the tracker callback is executed?
Or is Meteor smart enough to know when Meteor.subscribe is being called and does magic with the subscriptions?
The ReactMeteorData container runs createContainer's callback inside a reactive Tracker computation.
One of its features is stopping the subscription if the computation is invalidated or stopped.
If the function re-run produces an identical subscription, (same publication, same parameters) the library is smart enough and does not cancel and re-create the same subscription.
When the component is unmounted, the computation is stopped, the subscription is cancelled and not re-created (as the callback is not called again) and therefore automatically unsubscribed.
If you call Meteor.subscribe within a reactive computation, for example using Tracker.autorun, the subscription will automatically be cancelled when the computation is invalidated or stopped; it is not necessary to call stop on subscriptions made from inside autorun. However, if the next iteration of your run function subscribes to the same record set (same name and parameters), Meteor is smart enough to skip a wasteful unsubscribe/resubscribe.
(source: Meteor Docs)

Categories

Resources