React-admin: Realtime Saga (ra-realtime) cause infinite loop when unsubscribe

Created on 3 Nov 2018  路  3Comments  路  Source: marmelab/react-admin

What you were expecting:
When unsubscribe is triggered, calling observer.complete() should work normally like the README suggests.

What happened instead:
When unsubscribe is triggered, calling observer.complete() is causing unsubscribe to be triggered again and again until it crashed with uncaught at watchCrudActions RangeError: Maximum call stack size exceeded

Steps to reproduce:

  1. Setup the realtime saga as suggested by the example on README
  2. Add console.log("unsubscribe") on unsubscribe function to help debugging
  3. Make sure that the PostList page is shown
  4. Move away from PostList page to trigger unsubscribe. We could move to other page, or just click CreatePost button
  5. Observe your browser console

Related code:
I'm linking a code sandbox to illustrate the problem, but be warned that it will crash the session when we reproduce the steps above.

https://codesandbox.io/s/34jzjxpw9m

Environment

  • React-admin version: 2.4.0
  • ra-realtime version: 2.4.1
  • React version: 16.5.2
  • Browser: Chrome 70.0.3538.77
bug

Most helpful comment

Hi @alukito!
My solution works fine, here it is.

// In createRealtimeSaga.js
import realtimeSaga from 'ra-realtime';

const observeRequest = dataProvider => (type, resource, params) => {
  // Filtering so that only posts are updated in real time
  // if (resource !== 'posts') return;
  // Use your apollo client methods here or sockets or whatever else including the following very naive polling mechanism
  return {
    subscribe(observer) {
      let intervalId = setInterval(() => {
        dataProvider(type, resource, params)
          .then(results => observer.next(results)) // New data received, notify the observer
          .catch(error => observer.error(error)); // Ouch, an error occured, notify the observer
      }, 5000);

      const subscription = {
        unsubscribe() {
          if (intervalId) {
            // Clean up after ourselves
            clearInterval(intervalId);
            intervalId = undefined;
            // Notify the saga that we cleaned up everything
            observer.complete();
          }
        },
      };

      return subscription;
    },
  };
};

export default dataProvider => realtimeSaga(observeRequest(dataProvider));

may be there is need to change documentation sample.

All 3 comments

Hi @alukito!
My solution works fine, here it is.

// In createRealtimeSaga.js
import realtimeSaga from 'ra-realtime';

const observeRequest = dataProvider => (type, resource, params) => {
  // Filtering so that only posts are updated in real time
  // if (resource !== 'posts') return;
  // Use your apollo client methods here or sockets or whatever else including the following very naive polling mechanism
  return {
    subscribe(observer) {
      let intervalId = setInterval(() => {
        dataProvider(type, resource, params)
          .then(results => observer.next(results)) // New data received, notify the observer
          .catch(error => observer.error(error)); // Ouch, an error occured, notify the observer
      }, 5000);

      const subscription = {
        unsubscribe() {
          if (intervalId) {
            // Clean up after ourselves
            clearInterval(intervalId);
            intervalId = undefined;
            // Notify the saga that we cleaned up everything
            observer.complete();
          }
        },
      };

      return subscription;
    },
  };
};

export default dataProvider => realtimeSaga(observeRequest(dataProvider));

may be there is need to change documentation sample.

Hi @vedmalex, thank you for your hint. I agree that we could use some condition to stop the recursion, but I don't think calling observer.complete() should trigger unsubscribe function again. It forces us to handle the intervalId state instead of just calling clearInterval in your example. I believe there is some unintended behaviour happening here.

We won't address that bug in 2.x, and ra-realtime was not ported to v3. The port will probably not use sagas.

If anyone from the community wants this fixed in 2.x, please open a PR with a patch.

Was this page helpful?
0 / 5 - 0 ratings