SHARE
TWEET

Untitled

a guest Jul 17th, 2017 52 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /** @jsx h */
  2.  
  3. import { h, Component, cloneElement } from "preact";
  4.  
  5. export function areEqual(a, b) {
  6.   if (!a && !b) return true;
  7.   if (!a || !b) return false;
  8.   if (Object.keys(a).length !== Object.keys(b).length) return false;
  9.  
  10.   return Object.keys(a).every(function(key) {
  11.     return a[key] === b[key];
  12.   });
  13. }
  14.  
  15. class ReactRelayQueryRenderer extends Component {
  16.   constructor(props, context) {
  17.     super(props, context);
  18.     let {query, variables} = props;
  19.     const environment = props.environment;
  20.     let operation = null;
  21.     if (query) {
  22.       const {
  23.         createOperationSelector,
  24.         getOperation,
  25.       } = environment.unstable_internal;
  26.       query = getOperation(query);
  27.       operation = createOperationSelector(query, variables);
  28.       variables = operation.variables;
  29.     }
  30.  
  31.     this._mounted = false;
  32.     this._operation = operation;
  33.     this._pendingFetch = null;
  34.     this._relayContext = {
  35.       environment,
  36.       variables
  37.     };
  38.     this._rootSubscription = null;
  39.     this._selectionReference = null;
  40.     if (query) {
  41.       this.state = {
  42.         readyState: getDefaultState(environment, operation, this, props.cacheConfig)
  43.       };
  44.     } else {
  45.       this.state = {
  46.         readyState: {
  47.           error: null,
  48.           props: {},
  49.           retry: null
  50.         }
  51.       };
  52.     }
  53.  
  54.     if (operation && !this.state.readyState.props) {
  55.       this._fetch(operation, props.cacheConfig);
  56.     }
  57.   }
  58.  
  59.   componentDidMount() {
  60.     this._mounted = true;
  61.   }
  62.  
  63.   componentWillReceiveProps(nextProps) {
  64.     if (
  65.       nextProps.query !== this.props.query ||
  66.       nextProps.environment !== this.props.environment ||
  67.       !areEqual(nextProps.variables, this.props.variables)
  68.     ) {
  69.       const {query, variables} = nextProps;
  70.       const environment = nextProps.environment;
  71.       if (query) {
  72.         const {
  73.           createOperationSelector,
  74.           getOperation,
  75.         } = environment.unstable_internal;
  76.         const operation = createOperationSelector(
  77.           getOperation(query),
  78.           variables
  79.         );
  80.         this._operation = operation;
  81.         this._relayContext = {
  82.           environment,
  83.           variables: operation.variables
  84.         };
  85.         this._fetch(operation, nextProps.cacheConfig);
  86.         this.setState({
  87.           readyState: getDefaultState()
  88.         });
  89.       } else {
  90.         this._operation = null;
  91.         this._relayContext = {
  92.           environment,
  93.           variables
  94.         };
  95.         this._release();
  96.         this.setState({
  97.           readyState: {
  98.             error: null,
  99.             props: {},
  100.             retry: null
  101.           }
  102.         });
  103.       }
  104.     }
  105.   }
  106.  
  107.   componentWillUnmount() {
  108.     this._release();
  109.     this._mounted = false;
  110.   }
  111.  
  112.   shouldComponentUpdate(nextProps, nextState) {
  113.     return (
  114.       nextProps.render !== this.props.render ||
  115.       nextState.readyState !== this.state.readyState
  116.     );
  117.   }
  118.  
  119.   _release() {
  120.     if (this._pendingFetch) {
  121.       this._pendingFetch.dispose();
  122.       this._pendingFetch = null;
  123.     }
  124.     if (this._rootSubscription) {
  125.       this._rootSubscription.dispose();
  126.       this._rootSubscription = null;
  127.     }
  128.     if (this._selectionReference) {
  129.       this._selectionReference.dispose();
  130.       this._selectionReference = null;
  131.     }
  132.   }
  133.  
  134.   _fetch(operation, cacheConfig) {
  135.     const {environment} = this.props;
  136.  
  137.     // Immediately retain the results of the new query to prevent relevant data
  138.     // from being freed. This is not strictly required if all new data is
  139.     // fetched in a single step, but is necessary if the network could attempt
  140.     // to incrementally load data (ex: multiple query entries or incrementally
  141.     // loading records from disk cache).
  142.     const nextReference = environment.retain(operation.root);
  143.  
  144.     let readyState = getDefaultState();
  145.     let snapshot; // results of the root fragment
  146.     const onCompleted = () => {
  147.       this._pendingFetch = null;
  148.     };
  149.     const onError = error => {
  150.       readyState = {
  151.         error,
  152.         props: null,
  153.         retry: () => {
  154.           this._fetch(operation, cacheConfig);
  155.         }
  156.       };
  157.       if (this._selectionReference) {
  158.         this._selectionReference.dispose();
  159.       }
  160.       this._pendingFetch = null;
  161.       this._selectionReference = nextReference;
  162.       this.setState({readyState});
  163.     };
  164.     const onNext = () => {
  165.       // `onNext` can be called multiple times by network layers that support
  166.       // data subscriptions. Wait until the first payload to render `props` and
  167.       // subscribe for data updates.
  168.       if (snapshot) {
  169.         return;
  170.       }
  171.       snapshot = environment.lookup(operation.fragment);
  172.       readyState = {
  173.         error: null,
  174.         props: {
  175.           ...snapshot.data,
  176.           children: this.props.children
  177.         },
  178.         retry: () => {
  179.           this._fetch(operation, cacheConfig);
  180.         }
  181.       };
  182.  
  183.       if (this._selectionReference) {
  184.         this._selectionReference.dispose();
  185.       }
  186.       this._rootSubscription = environment.subscribe(snapshot, this._onChange);
  187.       this._selectionReference = nextReference;
  188.       this.setState({readyState});
  189.     };
  190.  
  191.     if (this._pendingFetch) {
  192.       this._pendingFetch.dispose();
  193.     }
  194.     if (this._rootSubscription) {
  195.       this._rootSubscription.dispose();
  196.     }
  197.     const request = environment.streamQuery({
  198.       cacheConfig,
  199.       onCompleted,
  200.       onError,
  201.       onNext,
  202.       operation
  203.     });
  204.     this._pendingFetch = {
  205.       dispose() {
  206.         request.dispose();
  207.         nextReference.dispose();
  208.       }
  209.     };
  210.   }
  211.  
  212.   _onChange = (snapshot) => {
  213.     this.setState({
  214.       readyState: {
  215.         ...this.state.readyState,
  216.         props: {
  217.           ...snapshot.data,
  218.           children: this.props.children
  219.         }
  220.       }
  221.     });
  222.   };
  223.  
  224.   getChildContext() {
  225.     return {
  226.       relay: this._relayContext
  227.     };
  228.   }
  229.  
  230.   render() {
  231.     // Note that the root fragment results in `readyState.props` is already
  232.     // frozen by the store; this call is to freeze the readyState object and
  233.     // error property if set.
  234.     return this.props.render(this.state.readyState);
  235.   }
  236. }
  237.  
  238. function getDefaultState(environment, operation, component, cacheConfig) {
  239.   const emptyState = {
  240.     error: null,
  241.     props: null,
  242.     retry: null
  243.   };
  244.   if (environment && operation) {
  245.     const satisfied = environment.check(operation.fragment);
  246.     if (satisfied) {
  247.       const snapshot = environment.lookup(operation.fragment);
  248.       return {
  249.         error: null,
  250.         props: {
  251.           ...snapshot.data,
  252.           children: component.props.children
  253.         },
  254.         retry: () => {
  255.           component._fetch(operation, cacheConfig);
  256.         }
  257.       };
  258.     } else {
  259.       return emptyState;
  260.     }
  261.   } else {
  262.     return emptyState;
  263.   }
  264. }
  265.  
  266. export default ReactRelayQueryRenderer;
RAW Paste Data
Top