Advertisement
Guest User

Untitled

a guest
Jul 17th, 2017
87
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.61 KB | None | 0 0
  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;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement