Cancel graphQL call on component unmount - javascript

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.

Related

Updating local and parent state causes unmounted component error

I have a table with buttons. I want to track the number of times each individual button is clicked along with the total number of times any button has been clicked. I can only get one-or-the-other to work.
I attempt to accomplish this by having a "global" state hook that is passed into each row of the table used for tracking total clicks. I also have a "local" state hook that is part of each component that makes up a row in the table for tracking individual clicks. I pass the "global" state from the parent to the children rows as a pair of callbacks (one for the getter, one for the setter).
Error Message:
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.
in ButtonCell (created by clickableButtonCell)
in clickableButtonCell (created by Table)
Problematic Code:
export default function ButtonCell({
totalButtonClicks,
setTotalButtonClicks
}) {
// Having this local state variable causes the unmounted component error.
// Removing this state and its references will cause the error to go away.
// Or, removing the state variable passed into this component will cause
// the error to go away and the individualClicks state will work as intended.
const [individualClicks, setIndividualClicks] = useState(0);
const onClick = async () => {
await axios.get("www.github.com").then(() => {
setTotalButtonClicks(totalButtonClicks + 1);
setIndividualClicks(individualClicks + 1);
return null;
});
};
return (
<button type="button" onClick={onClick}>
Click Me! {individualClicks}
</button>
);
}
Code Sandbox: https://codesandbox.io/s/cranky-sun-wflot?file=/src/ButtonCell.js
Edit: https://codesandbox.io/s/hopeful-wing-7cwio?file=/src/ButtonCell.js
useMemo in the MyTable.js was causing rerender because of the props change (caused by setting state).

Is it necessary to unmount state within ComponentWillUnmount?

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);
}

How to cause the component to re-render after an ajax request

Rerendering same component inside one function in ReactJs. I'm adding comment and now after that I want to rerender the same component. Can anyone help?
commentPost() {...
axios.defaults.headers.common['Authorization'] = getToken;
axios.post(apiBaseUrl+'v1/comments/post-comment',input)
.then(function (response) {
console.log("Comment Post",response);
//here I want to rerender this component itself
})
.catch(function (error) {
console.log(error);
});...
There are two things that you can do to cause the same component to render
First: Update a state of the current component using the setState method.
setState() will always lead to a re-render unless
shouldComponentUpdate() returns false. If mutable objects are being
used and conditional rendering logic cannot be implemented in
shouldComponentUpdate(), calling setState() only when the new
state differs from the previous state will avoid unnecessary
re-renders.
Second: You can call forceUpdate in case you do not want to update any state of the current component. You can call it like this.forceUpdate().
Calling forceUpdate() will cause render() to be called on the
component, skipping shouldComponentUpdate(). This will trigger the
normal lifecycle methods for child components, including the
shouldComponentUpdate() method of each child. React will still only
update the DOM if the markup changes.
Assuming that you are performing an async request from which you are getting a response you would want to update the component's state with the data that you got and hence setState would be the way forward for you

Call componentDidMount when API responds

In my project I have a call to an action that makes a webservice call and in turn dispatch actions to the result of the ws, these actions edit the store.
My problem is in :
ComponentDidUpdate () {
If (this.props.messages.length) {
Const items = this.props.messages.filter (this.isDisplayable);
This.timer = setInterval (() => {
If (items.length> 0) {
This.props.popItem (items);
} Else {
ClearInterval (this.timer);
}
}, This.props.interval);
}
}
In fact it is launched several times and I have warnings of
Warning: flattenChildren (...): Encountered two children with the same
key, 1. Child keys must be unique; When two children share a key,
only the first child will be used.
I used the componentDidMount but it launches it before api responds.
my question is:
Is that there is a way to update the component only at the response of my action, or alternatively to pass the warnings ?
try this :
componentWillReceiveProps(nextProps) {
if (this.props.messages === nextProps.messages) return;
i had some probleme and i resolve it by force update
forceUpdate () {
If (this.props.messages.length) {
...
}
}
In my project I have a call to an action that makes a webservice call and in turn dispatch actions to the result of the ws, these actions edit the store.
None of the methods componentDidMount and componentDidUpdate are good.
Observe the Store in Redux and update your component accordingly when the correct action TYPE is found.
Since you are using the Redux architecture, the state for all your components is in a single place — in the Store.
yes i know, but the problem is that componentDidUpdate is called several times which gives me the index error.
This is quite normal in React. Check this lifecycle.
What you should do is the govern the Redux architecture.
I will try today to provide some diagrams for you.
In general, anything you do will be from the global Store.
You may forget the React.Component state, and props you had in the non-Redux applications.
You typically need to use the Wrapper as a context provider around your app, where the context is the property of React.Component.
The context will be passed to all children and grandchildren so this will be the global Store organization.
Then you will need to read the Store from the context, and call the two typical methods: dispatch and subscribe.

react-redux store not updating within onClick function

I'm experiencing this weird issue where my react-redux store is updating, but is not updating within the function that calls the actions.
this.props.active is undefined, then I set it to an integer with this.props.actions.activeSet(activeProc), but it remains undefined and enters the next if condition.
I know my app is working because everything else works with this.props.active having the correct value.
Is this supposed to happen?
edit:
After doing some testing, it appears that the state remains the same inside the onClick function.
All calls to console.log(this.props) made within the onClick function show no change to the state, but adding setTimeout(() => {console.log(this.props)}, 1) at the end to test shows that the state is being updated.
Other parts of the app are working as intended, with state changes applied immediately.
But I still don't understand what is going on.
Component function code
() => {
console.log(this.props.active); // undefined
if (this.props.active === undefined && this.props.readyQueue.length > 0) {
let activeProc = this.props.readyQueue[0];
this.props.actions.readyPop();
this.props.actions.activeSet(activeProc); // set to an integer
this.props.actions.execStateSet("Running");
}
console.log(this.props.active); // still remains undefined
if (this.props.active === undefined) {
this.props.actions.execStateSet("Idle");
}
}
function mapStateToProps(state, props) {
return {
active: state.ProcessReducer.active,
};
}
Action code
export const activeSet = (procId) => {
return {
type: 'ACTIVE_SET',
procId
}
}
Reducer code
case 'ACTIVE_SET':
return Object.assign({}, state, {
active: action.procId
});
Your Redux state updates synchronously with the dispatch of your action. Your reducer has executed by the time the dispatch call returns.
However, React isn't Redux. Redux tells React-Redux's wrapper component that the state has changed. This also happens before dispatch returns.
React-Redux then tells React that the component needs to be rerendered by calling forceUpdate. React then waits until it feels it's a good time to take care of that. I haven't looked, but it probably uses setImmediate or equivalent but it's async. This allows React to batch updates and maybe there are other reasons.
In any case, the React-Redux wrapper component will get rendered by React when the time comes and it'll use your mapStateToProps to distill theprops out of the state and then passes them to React as props for your actual component. Then, when React feels it's an okay time, it calls your render method or function. It may do all kinds of things in before that, such as calling componentWillReceiveProps or rendering some other component that also needs rendering. In any case it's none of our business. React does its thing. But when your Render function is called, your props will now reflect the new state.
You shouldn't rely on new state in an onClick handler. The onClick should only call the bound action creator, which I guess is now more aptly called an action dispatcher. If something needs to be done with the new state, you should use Redux-Thunk middleware and create a thunked action creator. These have access to getState and if they don't perform any internal async stuff, then the entire action can actually be just as synchronous as a simple dispatch (not that you'd need that in a simple onClick handler).
Finally, React is very asynchronous in nature. Think of it as telling React what you want (component + props) and letting React take it from there. If React needs to know how to turn a component into DOM elements, it'll call your component's render function. How or when React does is thing is an implementation detail that doesn't concern us.

Categories

Resources