Skip to content

Warning: Can't perform a React state update on an unmounted component #1515

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
sashahavia opened this issue Apr 28, 2020 · 51 comments · Fixed by #1544
Closed

Warning: Can't perform a React state update on an unmounted component #1515

sashahavia opened this issue Apr 28, 2020 · 51 comments · Fixed by #1544

Comments

@sashahavia
Copy link

I am using GraphiQL in the form as a textarea field and every time I hit submit and navigate away from the page I get this warning

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 GraphiQL (created by GraphQlTextArea)

Can someone please look into this?

@acao
Copy link
Member

acao commented Apr 29, 2020

sounds like it might be an issue with the current usage of componentWillIUnmount. what version of graphiql and react are you using? if you update to the 1.0.0-alpha.n versions of GraphiQL, we use react 16 and hooks and start to replace some of those methods.

if that doesn't help, we are in the midst of a rewrite, so after this PR is merged that lifecycle method won't be used at all anymore:

#1468

@acao acao added the bug label Apr 29, 2020
@sashahavia
Copy link
Author

sashahavia commented Apr 29, 2020

I am using graphiql version 0.17.5 and react version 16.8.6

Ok, I will try updating to the 1.0.0-alpha.n versions

@acao
Copy link
Member

acao commented Apr 30, 2020

ok yeah, lemme know! that should be the most progressive path. if that doesn’t work, graphiql 0.x.y versions seem to work best with react 15

@sashahavia
Copy link
Author

sashahavia commented Apr 30, 2020

I tried 1.0.0-alpha.8 version of graphiql and still getting the error. Unfortunately can't switch to react 15. Looking forward to the rewrite.

@acao
Copy link
Member

acao commented May 1, 2020

@sashahavia yeah it's all still there:
https://github.com/graphql/graphiql/blob/master/packages/graphiql/src/components/GraphiQL.tsx#L351

our bad, we should have removed this a while ago. I think in react 16 at some minor version along the way they introduced it. don't usually see this because i don't test with any examples that programatically mount/unmount, GraphiQL is usually the root component in all of our examples!

if you fork master branch, you could remove the entire componentWillUnmount, or I can just kick out another alpha version with that removed, is that ok?

@acao
Copy link
Member

acao commented May 1, 2020

this is why the upgrade to react 16 was still in alpha... I knew there'd be issues like this! thanks so much for raising it. I think I'll just make another quick alpha release to fix this before we merge the rewrite

@sashahavia
Copy link
Author

If you can make another alpha release to fix that that would be great

@acao
Copy link
Member

acao commented May 1, 2020

@sashahavia i'm still having trouble reproducing the issue, can you provide the steps to produce this bug? perhaps you can document the bug by forking this codesandbox?

@sashahavia
Copy link
Author

The error happens only when you introduce useEffect

useEffect(() => {
    props.getEndpoints();
}, []);

@acao
Copy link
Member

acao commented May 2, 2020

when you useEffect in a parent component? props.getEndpoints() - this is something specific to your implementation, right? let me see if i can reproduce so i can fix it, thanks for your patience!

@acao
Copy link
Member

acao commented May 2, 2020

Here's a demo :
https://codesandbox.io/s/graphiql-js-next-example-qsh7h?file=/src/index.js

seems to be working ok with a basic console.log()

so possibly your props.getEndpoints() is doing something that triggers a bug that still may be on our side? does it write to state or use context or anything like that?

@sashahavia
Copy link
Author

Yes, when I do console.log() in my implementation I also don't get an error

It only happens when I actually call my redux action to get all endpoints. My action makes an axios request

@sashahavia
Copy link
Author

Not sure if this helps but here is the full stack trace of the warning

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 GraphiQL (created by GraphQlStatement)
    in div (created by GraphQlStatement)

r | @ | react_devtools_backend.js:6
-- | -- | --
  | warningWithoutStack | @ | vendors~main.bundle.js:114722
  | warnAboutUpdateOnUnmounted | @ | vendors~main.bundle.js:132691
  | scheduleWork | @ | vendors~main.bundle.js:134101
  | enqueueSetState | @ | vendors~main.bundle.js:125385
  | push.../../node_modules/react/cjs/react.development.js.Component.setState | @ | vendors~main.bundle.js:155038
  | (anonymous) | @ | vendors~main.bundle.js:50653
  | Promise.then (async) |   |  
  | push.../../node_modules/graphiql/esm/components/GraphiQL.js.GraphiQL.fetchSchema | @ | vendors~main.bundle.js:50646
  | push.../../node_modules/graphiql/esm/components/GraphiQL.js.GraphiQL.componentDidMount | @ | vendors~main.bundle.js:50405
  | commitLifeCycles | @ | vendors~main.bundle.js:131550
  | commitAllLifeCycles | @ | vendors~main.bundle.js:132952
  | callCallback | @ | vendors~main.bundle.js:114365
  | invokeGuardedCallbackDev | @ | vendors~main.bundle.js:114415
  | invokeGuardedCallback | @ | vendors~main.bundle.js:114472
  | commitRoot | @ | vendors~main.bundle.js:133164
  | (anonymous) | @ | vendors~main.bundle.js:134634
  | unstable_runWithPriority | @ | vendors~main.bundle.js:168544
  | completeRoot | @ | vendors~main.bundle.js:134633
  | performWorkOnRoot | @ | vendors~main.bundle.js:134562
  | performWork | @ | vendors~main.bundle.js:134470
  | performSyncWork | @ | vendors~main.bundle.js:134444
  | requestWork | @ | vendors~main.bundle.js:134313
  | scheduleWork | @ | vendors~main.bundle.js:134127
  | scheduleRootUpdate | @ | vendors~main.bundle.js:134788
  | updateContainerAtExpirationTime | @ | vendors~main.bundle.js:134816
  | updateContainer | @ | vendors~main.bundle.js:134873
  | push.../../node_modules/react-dom/cjs/react-dom.development.js.ReactRoot.render | @ | vendors~main.bundle.js:135169
  | (anonymous) | @ | vendors~main.bundle.js:135306
  | unbatchedUpdates | @ | vendors~main.bundle.js:134675
  | legacyRenderSubtreeIntoContainer | @ | vendors~main.bundle.js:135302
  | render | @ | vendors~main.bundle.js:135371
  | ./main.js | @ | main.bundle.js:23273
  | __webpack_require__ | @ | main.bundle.js:785
  | fn | @ | main.bundle.js:151
  | 0 | @ | main.bundle.js:32553
  | __webpack_require__ | @ | main.bundle.js:785
  | checkDeferredModules | @ | main.bundle.js:46
  | (anonymous) | @ | main.bundle.js:861
  | (anonymous) | @ | main.bundle.js:864


@acao
Copy link
Member

acao commented May 2, 2020

hmm, trying to emulate what may be happening and I still don't quite see it - maybe it's re-mounting because of multiple incoming props changes, which would invoke willUmount, and my guess is this is happening just as redux is performing it's async fetch operation. It seems in React 16 they have safegaurds against this that weren't there for the old react 15 lifecycle, which is why we are just seeing this with the new react hooks components? Just a theory. Here's my attempt:

https://codesandbox.io/s/graphiql-js-next-example-qsh7h?file=/src/index.js

i have an idea for a quick potential hack though, but I cannot test it because I cannot reproduce this.

try this beneath where you import GraphiQL component:

GraphiQL.prototype.componentWillUnmount = undefined;

(when i try that it runs fine)

I think maybe this indeed is the culprit as suspected, and I could remove the method and re-release without being able to test myself, but this would allow you to test much more quickly

@acao
Copy link
Member

acao commented May 4, 2020

@sashahavia did you have any luck with that?

@acao
Copy link
Member

acao commented May 4, 2020

Just want to chase down this bug as it seems to be the only bug stopping us from our stable release for 1.0.0, and then merge a bunch of breaking changes we have waiting.

I don't think I'll be able to reproduce this bug it seems like it's timing related, so that line above is what I suspect will prove that we just need to remove that entire lifecycle method

@sashahavia
Copy link
Author

Looks like GraphiQL.prototype.componentWillUnmount = undefined; helped

@acao
Copy link
Member

acao commented May 4, 2020

Great! ok, i'll cut an alpha release today for you to try out. Didn't want to remove that functionality unless it fixed this issue. Thanks a bunch for helping me track it down!

@acao acao added this to the GraphiQL-1.0.0 milestone May 4, 2020
@acao
Copy link
Member

acao commented May 4, 2020

hmm, looks like this fix will break more functionality than I had thought, particularly with persisting ui state. this is the only place many of these values are set. is that temporary fix O.K. until I can spend some more time on this and cut another alpha later this week?

@sashahavia
Copy link
Author

sashahavia commented May 4, 2020

No worries. I can wait. It doesn't break the functionality for me, it is just a warning. And by the way GraphiQL.prototype.componentWillUnmount = undefined; - the warning still pops up but not as often

I was thinking is it possible to add support for canceling all requests. I think that's what actually breaking.

I am using axios in my fetcher and it would be nice if GraphiQL could accept cancel function and then call it in componentWillUnmount

const source = axios.CancelToken.source();

And then in GraphiQL

<GraphiQL ref={c => graphiql = c} fetcher={createFetcher(api)} query={query} onEditQuery={() => updateQuery()} cancel={source.cancel()}>

And for the fetch request we can use

const controller = new AbortController();
const { signal } = controller;

<GraphiQL ref={c => graphiql = c} fetcher={createFetcher(api)} query={query} onEditQuery={() => updateQuery()} cancel={controller.abort()}>

cancel function can be optional parameter for GraphiQL

When I look at the network tab I can see multiple requests being fired at the same time by fetcher function, it would be nice to cancel requests when unMounting since the user can move away from the page before requests are finished

@acao
Copy link
Member

acao commented May 4, 2020

cancellable operation HTTP requests and WSS subscriptions sound fantastic. this could be added to the default fetcher in the rewrite we are about to merge that will replace a lot of this.

the breaking changes from removing componentWillMount in terms of persisting UI state will noticeably impact other users and implementations unfortunately, so we will still need to persist that state, and I will do so by using a more appropriate place in the lifecycle to persist state.

also worth noting these changes will all be discarded for the 2.0.0 rewrite, as we are changing how we persist state.

if you think the cancellable requests solve it, can you get it working without setting componentWillMount to undefined?

a fetcher should not be making multiple requests each time a request is fired, that sounds like an implementation issue. try here: https://graphiql-test.netlify.app/

@sashahavia
Copy link
Author

In my case multiple requests happen because I am using it within Redux form.

 <Field
                label="GraphQL Statement"
                name="inputJson"
                query={query}
                change={change}
                endpoints={endpoints}
                component={GraphQlTextArea}
            />

GraphQlTextArea has GraphiQL with fetcher implementation

@acao
Copy link
Member

acao commented May 7, 2020

thank you! very helpful to know

@acao
Copy link
Member

acao commented May 21, 2020

for a contributor, steps to fix this bug while not breaking features are:

  1. use master branch
  2. move all functionality in GraphiQL.componentWillUnmount to the respective events
  3. for events that use event listeners, use our existing debounce logic - for example, a drag event may trigger 100 times, but we only need to persist once, a few seconds after the user has completed dragging.

@acao
Copy link
Member

acao commented May 28, 2020

@sashahavia we released another alpha today that should take care of this, by avoiding fetch requests during re-renders. [email protected] should be the one you want

let me know if that helps! if not, we will try the other fix as well.

@sashahavia
Copy link
Author

sashahavia commented May 29, 2020

@acao I didn't have a chance to test it this week. Will try to test it first thing Monday.

@zoontek
Copy link

zoontek commented Jun 4, 2020

I can confirm that [email protected] breaks the webpack compilation.

You can add a resolution in your package.json until it's fixed:

"resolutions": {
  "graphql-language-service-interface": "2.4.0-alpha.8"
}

@sashahavia
Copy link
Author

@zoontek Getting these errors now

ERROR in ./node_modules/codemirror-graphql/lint.js
Module not found: Error: Can't resolve 'graphql-language-service-interface' in '/.../.../Desktop/.../.../node_modules/codemirror-graphql'
 @ ./node_modules/codemirror-graphql/lint.js 5:39-84
 @ ./node_modules/graphiql/esm/components/QueryEditor.js
 @ ./node_modules/graphiql/esm/components/GraphiQL.js
 @ ./node_modules/graphiql/esm/index.js
...
 @ multi ./main.js webpack-hot-middleware/client

ERROR in ./node_modules/codemirror-graphql/hint.js
Module not found: Error: Can't resolve 'graphql-language-service-interface' in '/.../.../Desktop/.../.../node_modules/codemirror-graphql'
 @ ./node_modules/codemirror-graphql/hint.js 5:39-84
 @ ./node_modules/graphiql/esm/components/QueryEditor.js
 @ ./node_modules/graphiql/esm/components/GraphiQL.js
 @ ./node_modules/graphiql/esm/index.js
...
 @ multi ./main.js webpack-hot-middleware/client

@acao
Copy link
Member

acao commented Jun 4, 2020

i can't seem to reproduce this bug with our webpack example in master (see webpack config and babel config)

so i will go ahead and publish again from master.

@acao
Copy link
Member

acao commented Jun 4, 2020

@sashahavia is this still an issue with the latest alpha.13 release?

@sashahavia
Copy link
Author

@acao alpha.13 compiled successfully

Unfortunately, I am still seeing the 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.

@acao
Copy link
Member

acao commented Jun 4, 2020

@sashahavia ok! the PR that we thought might fix it had that same error, but I think we had just reproduced it in a different context. gonnna try this other PR, #1544. thanks for helping with this!

@acao acao closed this as completed in #1544 Jun 5, 2020
@acao acao reopened this Jun 11, 2020
@acao
Copy link
Member

acao commented Jun 11, 2020

@sashahavia apologies this closed automatically. do you want to see if the 1.0.0 release resolves all of these issues for you?

@sashahavia
Copy link
Author

@acao I have updated to 1.0.0 release and still got this error

react_devtools_backend.js:6 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.

@acao
Copy link
Member

acao commented Jun 16, 2020

but we got rid of componentWillUnmount 😿

i'll work on a reproduction of this for tomorrow. 2.0.0 redesign and plugin api on hold! need to get 1.0.x right

@acao
Copy link
Member

acao commented Jun 17, 2020

are you using componentWillUnmount anywhere by chance?

@sashahavia
Copy link
Author

@acao We are suing React hooks and I don't have unmount hook in any of the components that I am using graphiql

@acao
Copy link
Member

acao commented Jun 17, 2020

we have several other componentWillUnmount methods in other components, primarily to remove event listeners. network cancellation didn't seem to address it either (cancelling schema fetch on unmount). the contributor who made the PR for network cancellation was able to reproduce it, so I'm seeing if he can help me figure it out, as i spent most of last night and this morning trying to reproduce this bug.

thinking about it more though... if it is related to the schema fetch, one workaround you might try is fetching the schema manually from elsewhere in the component tree, and passing it to <GraphiQL schema={schema} /> if you aren't already doing that. Then, it won't try to fetch the schema on mount on every "hard" re-render.

@acao
Copy link
Member

acao commented Jun 19, 2020

@sashahavia let me know how that workaround goes, and [email protected] has a fix that may help you as well, fwiw

@sashahavia
Copy link
Author

sashahavia commented Jun 22, 2020

@acao The workaround I have tried originally and it didn't help. I just tried [email protected] and still getting the same error. I also tried the workaround with the new version [email protected] and still getting the same error

@acao
Copy link
Member

acao commented Jun 22, 2020

@sashahavia the original case where someone was able to re-produce a similiar error was resolved, so without a way to re-produce the errors, we're kinda stuck. if you can create a codesandbox that re-creates the error then we should be able to help you.

@zoontek i see you are subscribing to this issue. are you having a similar issue?

@sashahavia
Copy link
Author

@acao Hey, sorry it took me so long. Here is the link to sandbox to re-produce the error

https://codesandbox.io/s/goofy-brook-kvren

@acao
Copy link
Member

acao commented Jul 16, 2020

fanatstic, was able to reproduce with this example, thank you!

@acao
Copy link
Member

acao commented Jul 16, 2020

now i can't reproduce it again, hahaha! what are the exact steps with this demo?

@sashahavia
Copy link
Author

@acao type something in Graphiql, add name and click submit. The error sometimes a little delayed. But I could see it both in sandbox console and chrome console tab

@acao
Copy link
Member

acao commented Jul 16, 2020

ok got it! i was doing the first two backwards. i also see that the active query goes blank once the typing begins! maybe a few bugs exposed here. thank you

@harshithpabbati
Copy link
Contributor

harshithpabbati commented Jul 21, 2020

ok got it! i was doing the first two backwards. i also see that the active query goes blank once the typing begins! maybe a few bugs exposed here. thank you

Hey @acao,

I found why the active query goes blank when we start typing, that's because GraphiQL is passed with the query prop which is equal to "".

<GraphiQL ref={c => (graphiql = c)} fetcher={createFetcher(api)} query="">

@acao
Copy link
Member

acao commented Jul 22, 2020

@harshithpabbati yes I'm aware, thanks!

@acao acao added the graphiql label Nov 13, 2022
@acao acao moved this to Todo in GraphiQL Roadmap Nov 13, 2022
@dimaMachina
Copy link
Collaborator

Closing due to inactivity. Feel free to reply if you still need help.

@dimaMachina dimaMachina closed this as not planned Won't fix, can't repro, duplicate, stale May 4, 2025
@github-project-automation github-project-automation bot moved this from Todo to Done in GraphiQL Roadmap May 4, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Done
Development

Successfully merging a pull request may close this issue.

5 participants