Skip to content
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

Polling doesn't stop after component dismount #174

Open
gtkatakura opened this issue May 17, 2021 · 5 comments
Open

Polling doesn't stop after component dismount #174

gtkatakura opened this issue May 17, 2021 · 5 comments

Comments

@gtkatakura
Copy link

I am struggling with a problem when using polling. Seems that even after my component dismount, the polling keeps happening and never stops. It is a very hard problem to reproduce, but I find that when we have the StrictMode around the app, the problems happen all the time. To be able to create a reproducible example, I changed the relay-hook-example/todo of this repository here: gtkatakura/relay-hooks#polling-problem.

  1. I added support to cancel the request into the fetchQuery:

commit changes

    const controller = new AbortController();

    fetch('http://localhost:3000/graphql', {
      signal: controller.signal,
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        query: operation.text,
        variables,
      }),
    })
      .then(response => response.json())
      .then(data => {
        if (data.errors) {
          sink.error(data.errors);
          return;
        }
        sink.next(data);
        sink.complete();
      });

    return () => {
      controller.abort();
    };
  1. I changed the polling to 5 seconds and added a setTimeout into the back-end to resolve the response into 3 seconds

  2. I added a button to hide and show the component on the screen

  3. I enabled the StrictMode

So after these changes, you just need to run the todo example and click into hide to dismount the component with the user query. You will see that after the dismount, it will keep polling the query. And if you click into hide when is happening the fetch, it doesn't cancel the request. If you remove the StrictMode, it will cancel the request properly.

Screenshot from 2021-05-17 16-15-57

@gtkatakura
Copy link
Author

gtkatakura commented May 17, 2021

I am not sure if it is a problem with relay-hooks or relay-runtime, but when I click on the button hide without StrictMode, it calls the function getObservableForCachedRequest:

code

function getObservableForCachedRequest(
  requestCache: Map<RequestIdentifier, RequestCacheEntry>,
  cachedRequest: RequestCacheEntry,
): Observable<GraphQLResponse> {
  return Observable.create(sink => {
    const subscription = cachedRequest.subject.subscribe(sink);

    return () => {
      subscription.unsubscribe();
      const cachedRequestInstance = requestCache.get(cachedRequest.identifier);
      if (cachedRequestInstance) {
        const requestSubscription = cachedRequestInstance.subscription;
        if (
          requestSubscription != null &&
          cachedRequestInstance.subject.getObserverCount() === 0
        ) {
          requestSubscription.unsubscribe();
          requestCache.delete(cachedRequest.identifier);
        }
      }
    };
  });
}

This requestSubscription.unsubscribe() is responsible fpr clear the polling, but into StrictMode the expression cachedRequestInstance.subject.getObserverCount() returns 1 instead of 0, so it doesn't calls unsubscribe and the poll keeps alive forever.

@morrys
Copy link
Member

morrys commented May 17, 2021

Hi @gtkatakura,
It seems a problem like this #111 (comment)

However, tomorrow i will try your example.

@gtkatakura
Copy link
Author

gtkatakura commented May 17, 2021

I created another example without StrictMode which has the same problem: https://github.com/gtkatakura/polling-problem

It doesn't happen all the time, sometimes when I dismount the components it properly cancels the requests and the polling, but you just need to keep trying to hide/dismount the components while the requests are happening that the problem will happen.

@morrys
Copy link
Member

morrys commented May 18, 2021

Hi @gtkatakura,
I tried your sample project and the cause of your problem is Suspense.
relay-polling-error

In fact, as you can see from the GIF, if you show and hide the component slowly all the dispose are made correctly while if you click quickly, so before the component exits from the suspended state, the subscriptions are cleaned through a timer configured inside library (as you can see at the end of the GIF).

The problem should also be present in the official version of the hooks but I should have the time to create a test case in their repository to prove it (@jstejada, @rbalicki2)

Here you find the timer in relay-hooks:
https://github.com/relay-tools/relay-hooks/blob/master/src/FetchResolver.ts#L98

Here you find the timer in react-relay:
https://github.com/facebook/relay/blob/master/packages/react-relay/relay-hooks/QueryResource.js#L196

@jukben
Copy link

jukben commented Feb 21, 2022

Hey @morrys sorry for reviving this old issue, I tried to do the same what author of this issue but with default hooks from React Relay. It doesn't work, it looks like it never dispose the query. I tried to do it dirty with those relay-hooks and it works. It this something specific for this library? I'm quite confuse why would I need to use another library when React Relay provide its own hooks. Sorry if this sounds confusing would love to chat about it more if you have time 🙏

EDIT & my brain dump:

If you wouldn't mind I would love to get in touch https://twitter.com/jukben – you look like someone with plenty of Relay experiences and I have few question which you might have answers for. Would you be down for some chilled Zoom call? 🤙

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants