react-relay-network-modern icon indicating copy to clipboard operation
react-relay-network-modern copied to clipboard

Support relay polling

Open fdecampredon opened this issue 7 years ago • 10 comments

To use Relay Polling (poll in cache config) we have to wrap the promise in fetch function into an observable. I was able to do so this way :

import {
  Environment,
  RecordSource,
  Store,
  Network,
  Observable,
} from 'relay-runtime';
import {
  RelayNetworkLayer,
  ...
} from 'react-relay-network-modern/es';

const network = new RelayNetworkLayer([
 ....
]);

const fetchFunction = (...args) =>
  Observable.create(sink => {
    network.fetchFn(...args).then(
      data => {
        sink.next(data);
        sink.complete();
      },
      e => {
        sink.error(e);
        sink.complete();
      },
    );
  });

const relayEnvironment = new Environment({
  network: Network.create(fetchFunction, network.subscribeFn),
  store: new Store(new RecordSource()),
});

export default relayEnvironment;

Perhaps it should be supported natively in this plugin ?

fdecampredon avatar Sep 20 '18 19:09 fdecampredon

Yep, it should be supported! 👍 Do you have a full working demo? Or maybe want to make PR?

By your example I do not belive that it works. Cause long polling means several responses from fetch, but promise resolves only once.

nodkz avatar Sep 21 '18 03:09 nodkz

Yup that's what i thought but it works. I fail to explain while exactly but RelayObservable seems to resubscribe every time when the Network return an observable. (which make polling working)

fdecampredon avatar Sep 25 '18 14:09 fdecampredon

Is this still a feature your looking at implementing?

robertpitt avatar Feb 21 '19 00:02 robertpitt

@fdecampredon Does the 3.0.0 release solve this issue?

AnotherHermit avatar Apr 09 '19 12:04 AnotherHermit

I got polling to work with regular cacheConfig, but the problem is it seems to invalidate the cache middleware. What I think is missing is simply allowing to pass the poll option in the cache middleware here, and then we could have a cache that is useful and self-updates: https://github.com/relay-tools/react-relay-network-modern/blob/master/src/middlewares/cache.js#L20

My solution using the above Observable example is using a different cache than the middleware instance, from what I can tell in my tests.

I don't know flow, but wondering if a pull request would be accepted for the additional option I'm suggesting?

joelvh avatar Apr 29 '19 04:04 joelvh

Scratch what I said in my last comment. I've done some further investigation and what I'm trying to achieve may not be possible with Relay polling.

I want to use the cached data for quicker "response time" and at an interval have the polling update the cache asynchronously, and the updates flow through the components while rendered (using found-relay in my case).

Do you guys know how to achieve that?

joelvh avatar Apr 29 '19 05:04 joelvh

To solve it I think it would require some changes to both the current Network Layer and Cache Middleware so that the fetch can resolve multiple times. A big hurdle would be to make middleware compatible with next() being an observable, or treat the cache differently from other middleware.

A quick and dirty version would be to make the sink from https://github.com/relay-tools/react-relay-network-modern/blob/master/src/RelayNetworkLayer.js#L74 available to middleware, then it would be possible to make an early return by calling sink.next( <cached data here> ) but pass the request down the middleware chain and let in resolve normally as well. Calling sink.next( ... ) would of course skip any other middleware since it wouldn't bubble back again, so this would only work for the cache since it should probably be the first middleware anyways.

AnotherHermit avatar Apr 29 '19 13:04 AnotherHermit

Thanks for the input @AnotherHermit. I went a couple directions, including replicating the Relay poll functionality, and finally landed on this very succinct solution that works for any cache options. (I should note, I'm using found-relay and how it passes cacheConfig: { poll: 5000 } to Relay.)

Note that, by the time this fetch function is called, Relay has already internally replaced the poll option with force: true:

import { Network, Environment, RecordSource, Store, Observable } from 'relay-runtime'
import { cacheMiddleware } from 'react-relay-network-modern'

const store = new Store(new RecordSource())
const network = new RelayNetworkLayer([
  cacheMiddleware({
    ttl: 15 * 60 * 1000,
    clearOnMutation: true
  })
])

const fetchQuery = (operation, variables, cacheConfig, uploadables) => {
  const { force = false, ...cacheConfigRest } = cacheConfig
  let calls = 0

  return Observable.create((sink) => {
    const cacheOptions = (++calls === 0 || !force) ? cacheConfigRest : cacheConfig
    return network.fetchFn(operation, variables, cacheOptions, uploadables).subscribe(sink)
  })
}

const environment = new Environment({
  network: Network.create(fetchQuery, network.subscribeFn),
  store
})

export default environment

Updated June 6, 2019:

Updated the above example fetchQuery to a much more concise solution to which fetches and then retrieves from cache while polling in the background. Works really well for us.

joelvh avatar Apr 29 '19 23:04 joelvh

@AnotherHermit btw - why doesn't RelayNetworkLayer return an Observable? https://github.com/relay-tools/react-relay-network-modern/blob/v3.0.2/src/RelayNetworkLayer.js#L74

joelvh avatar Apr 29 '19 23:04 joelvh

When I changed the RelayNetworkLayer from returning a promise, I wanted to make sure that I didn't break anything so I made as few changes as possible. So no real reason really.

AnotherHermit avatar May 02 '19 06:05 AnotherHermit