UNPKG

found-relay

Version:
163 lines (138 loc) 3.94 kB
import { createOperationDescriptor, getRequest } from 'relay-runtime'; export default class QuerySubscription { constructor({ environment, query, variables, cacheConfig, fetchPolicy }) { this.onChange = snapshot => { this.updateReadyState({ error: null, props: snapshot.data, retry: this.retry }); }; this.retry = () => { this.retrying = true; this.retryingAfterError = !!this.readyState.error; this.dispose(); // eslint-disable-next-line @typescript-eslint/no-empty-function this.execute(() => {}); }; this.environment = environment; this.query = query; this.variables = variables; this.cacheConfig = cacheConfig; this.fetchPolicy = fetchPolicy; this.operation = createOperationDescriptor(getRequest(query), variables, cacheConfig); this.fetchPromise = null; this.selectionReference = null; this.pendingRequest = null; this.rootSubscription = null; this.retrying = false; this.retryingAfterError = false; this.readyState = { error: null, props: null, retry: null }; this.listeners = []; this.relayContext = { environment: this.environment, variables: this.operation.request.variables }; } updateCacheConfig(nextCacheConfig) { if (this.cacheConfig !== nextCacheConfig) { this.cacheConfig = nextCacheConfig; this.operation = createOperationDescriptor(getRequest(this.query), this.variables, this.cacheConfig); } } fetch() { if (!this.fetchPromise) { this.fetchPromise = new Promise(resolve => { this.execute(resolve); }); } return this.fetchPromise; } execute(resolve) { let snapshot; this.selectionReference = this.retain(); const onSnapshot = () => { if (snapshot) { return; } snapshot = this.environment.lookup(this.operation.fragment); this.onChange(snapshot); this.rootSubscription = this.environment.subscribe(snapshot, this.onChange); resolve(); }; const onError = error => { this.updateReadyState({ error, props: null, retry: this.retry }); resolve(); }; const useStoreSnapshot = !this.retrying && (this.fetchPolicy === 'store-and-network' || this.fetchPolicy === 'store-or-network') && this.environment.check(this.operation).status === 'available'; if (!(this.fetchPolicy === 'store-or-network' && useStoreSnapshot)) { try { this.pendingRequest = this.environment.execute({ operation: this.operation }).finally(() => { this.pendingRequest = null; }).subscribe({ next: onSnapshot, error: onError }); } catch (error) { onError(error); return; } } // Only use the store snapshot if the network layer doesn't synchronously // resolve a snapshot, to match <QueryRenderer>. if (!snapshot && useStoreSnapshot) { onSnapshot(); } if (!snapshot && this.retryingAfterError) { this.updateReadyState({ error: null, props: null, retry: null }); } } updateReadyState(readyState) { this.readyState = readyState; this.listeners.forEach(listener => { listener(); }); } subscribe(listener) { this.listeners.push(listener); } unsubscribe(listener) { this.listeners = this.listeners.filter(item => item !== listener); } retain() { return this.environment.retain(this.operation); } dispose() { this.fetchPromise = null; if (this.selectionReference) { this.selectionReference.dispose(); } if (this.pendingRequest) { this.pendingRequest.unsubscribe(); } if (this.rootSubscription) { this.rootSubscription.dispose(); } } getQueryName() { return this.operation.root.node.name; } }