react-relay-network-modern
react-relay-network-modern copied to clipboard
How can environment.retain() be used with the network layer to disable relay garbage collection?
Description
How can we achieve using relay's environment.retain() to retain the data of a certain query and disable garbage collection?
Reasoning:
- "If no component is rendering the local data and you want to manually retain it, you can do so by calling environment.retain()" - Relay docs
Example from relay documentation:
import {createOperationDescriptor, getRequest} from 'relay-runtime';
// Create a query that references that record
const localDataQuery = graphql`
query LocalDataQuery {
viewer {
notes {
__typename
}
}
}
`;
// Create an operation descriptor for the query
const request = getRequest(localDataQuery);
const operation = createOperationDescriptor(request, {} /* variables */);
// Tell Relay to retain this operation so any data referenced by it isn't garbage collected
// In this case, all the notes linked to the `viewer` will be retained
const disposable = environment.retain(operation);
// Whenever you don't need that data anymore and it's okay for Relay to garbage collect it,
// you can dispose of the retain
disposable.dispose();
I don't know if handling this on the network layer is a good idea.
How we'd know which operation we should retain or dispose? I mean, we can't retain all queries forever.
@felippepuhle
We can implement it as a feature on top of the QueryRenderer or fetchWithMiddleware function (relay's fetch func) (eg: a ttl). The query is then retained for the ttl period. If the ttl provided to the QueryRenderer is larger than the TTL for the cache we'd never face the issues were the store was updated and the network cache overwrote these updates.
How we'd know which operation we should retain or dispose?
- We only retain queries that have
ttl-- we dispose the Observables after the ttl expires
Assume this scenario:
- We have a connection of messages that we render via a query renderer
- We update the connection of messages via subscriptions
- The Messages Query renderer is unmounted AND mounted again
- The Messages Query renderer now uses the old query data that is store in the network layer's cache and doesn't render the data updated by the subscriptions. Since relay modern garbage collected the connection along with it's updates.
Note: react-relay-offline provides good example use cases
Oh, understood! Thanks for the explanation @A-Tokyo! It seems a really cool idea.
@felippepuhle Awesome!! (:
I was checking the codebase to see how I can implement it but faced a couple of issues that I hope I can overcome soon:
- How to pass data from the Query Renderer (the ttl option)
- How to get an environment instance to call retain on
example of how the retain code should look like:
/* @flow */
import { createOperationDescriptor, getRequest } from 'relay-runtime';
/**
* Retains data in the relay store to avoid garbage collection
*
* useful in case of subscriptions
*
* https://stackoverflow.com/questions/58022925/disable-relayjs-garbage-collection
*/
const retainInStore = ({
getEnvironment,
query,
variables,
}: {
getEnvironment: () => Environment,
query: string,
variables?: Object,
}): Disposable => {
// Create a relay request from the query
const request = getRequest(query);
// Create an operation descriptor for the query
const operation = createOperationDescriptor(request, variables);
// Tell Relay to retain this operation so any data referenced by it isn't garbage collected and return the disposable
return getEnvironment().retain(operation);
};
export default retainInStore;
if (opts.ttl) {
const { dispose } = retainInStore({ getEnvironment, query, variables });
const timeout = setTimeout(() => { dispose() }, ttl);
// we can call clearTimeout(timeout); when we want to cleanup
}