react-relay 0.0.0-main-99abbcde → 0.0.0-main-56cda151

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Relay v0.0.0-main-99abbcde
2
+ * Relay v0.0.0-main-56cda151
3
3
  *
4
4
  * Copyright (c) Meta Platforms, Inc. and affiliates.
5
5
  *
package/hooks.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Relay v0.0.0-main-99abbcde
2
+ * Relay v0.0.0-main-56cda151
3
3
  *
4
4
  * Copyright (c) Meta Platforms, Inc. and affiliates.
5
5
  *
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Relay v0.0.0-main-99abbcde
2
+ * Relay v0.0.0-main-56cda151
3
3
  *
4
4
  * Copyright (c) Meta Platforms, Inc. and affiliates.
5
5
  *
package/legacy.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Relay v0.0.0-main-99abbcde
2
+ * Relay v0.0.0-main-56cda151
3
3
  *
4
4
  * Copyright (c) Meta Platforms, Inc. and affiliates.
5
5
  *
@@ -3,49 +3,57 @@
3
3
 
4
4
  var usePreloadedQuery = require('../usePreloadedQuery');
5
5
  var useRelayEnvironment = require('../useRelayEnvironment');
6
+ var invariant = require('invariant');
6
7
  var _require = require('react'),
7
8
  use = _require.use,
9
+ useEffect = _require.useEffect,
10
+ useLayoutEffect = _require.useLayoutEffect,
8
11
  useMemo = _require.useMemo;
9
12
  var _require2 = require('relay-runtime'),
10
- __internal = _require2.__internal,
11
- ROOT_TYPE = _require2.ROOT_TYPE,
13
+ RelayModernEnvironment = _require2.Environment,
12
14
  createOperationDescriptor = _require2.createOperationDescriptor,
13
15
  getRequest = _require2.getRequest;
14
- var defaultGetDataID = __internal.defaultGetDataID,
15
- normalizeResponse = __internal.normalizeResponse;
16
- var committedRefs = new WeakSet();
16
+ var publishedQueryRefs = new WeakMap();
17
+ function getPublishedQueryRefs(environment) {
18
+ var refs = publishedQueryRefs.get(environment);
19
+ if (refs == null) {
20
+ refs = new WeakMap();
21
+ publishedQueryRefs.set(environment, refs);
22
+ }
23
+ return refs;
24
+ }
17
25
  var DEFAULT_STALE_MS = 30_000;
26
+ var useIsomorphicLayoutEffect = typeof window === 'undefined' ? useEffect : useLayoutEffect;
18
27
  function useQueryFromServer(query, queryRef, options) {
19
- var _options$staleThresho, _environment$_getData;
28
+ var _options$staleThresho, _environment$construc, _environment$construc2;
20
29
  var environment = useRelayEnvironment();
21
30
  var request = getRequest(query);
22
31
  var threshold = (_options$staleThresho = options === null || options === void 0 ? void 0 : options.staleThresholdMs) !== null && _options$staleThresho !== void 0 ? _options$staleThresho : DEFAULT_STALE_MS;
23
- var getDataID = (_environment$_getData = environment._getDataID) !== null && _environment$_getData !== void 0 ? _environment$_getData : defaultGetDataID;
24
32
  var response = use(queryRef._response);
25
33
  var isFresh = response.data != null && Date.now() - queryRef.fetchedAt <= threshold;
26
- var shouldCommit = isFresh && !committedRefs.has(queryRef);
34
+ !(environment instanceof RelayModernEnvironment) ? process.env.NODE_ENV !== "production" ? invariant(false, 'useQueryFromServer: expected a RelayModernEnvironment, got `%s`. ' + 'useQueryFromServer is currently only compatible with RelayModernStore.', (_environment$construc = (_environment$construc2 = environment.constructor) === null || _environment$construc2 === void 0 ? void 0 : _environment$construc2.name) !== null && _environment$construc !== void 0 ? _environment$construc : typeof environment) : invariant(false) : void 0;
35
+ var publishedRefs = getPublishedQueryRefs(environment);
36
+ var shouldCommit = isFresh && !publishedRefs.has(queryRef);
27
37
  if (shouldCommit) {
28
- var _environment$__log;
29
- committedRefs.add(queryRef);
30
38
  var operation = createOperationDescriptor(request, queryRef.variables);
31
39
  var responsePayload = {
32
40
  data: response.data,
33
41
  errors: response.errors
34
42
  };
35
- var relayPayload = normalizeResponse(responsePayload, operation.root, ROOT_TYPE, {
36
- getDataID: getDataID,
37
- treatMissingFieldsAsNull: false,
38
- deferDeduplicatedFields: false,
39
- log: (_environment$__log = environment.__log) !== null && _environment$__log !== void 0 ? _environment$__log : null,
40
- path: [],
41
- shouldProcessClientComponents: false
42
- }, false);
43
- environment.getStore().publish(relayPayload.source);
43
+ publishedRefs.set(queryRef, environment.publishWithDeferredNotify(operation, responsePayload));
44
44
  }
45
+ useIsomorphicLayoutEffect(function () {
46
+ var refs = getPublishedQueryRefs(environment);
47
+ var flushNotify = refs.get(queryRef);
48
+ if (flushNotify != null) {
49
+ refs.set(queryRef, null);
50
+ flushNotify();
51
+ }
52
+ }, [environment, queryRef]);
45
53
  var preloadedQuery = useMemo(function () {
46
54
  var _request$params$id;
47
55
  var isFreshAtMemo = response.data != null && Date.now() - queryRef.fetchedAt <= threshold;
48
- var useStore = isFreshAtMemo || committedRefs.has(queryRef);
56
+ var useStore = isFreshAtMemo || getPublishedQueryRefs(environment).has(queryRef);
49
57
  return {
50
58
  kind: 'PreloadedQuery_DEPRECATED',
51
59
  environment: environment,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "react-relay",
3
3
  "description": "A framework for building GraphQL-driven React applications.",
4
- "version": "0.0.0-main-99abbcde",
4
+ "version": "0.0.0-main-56cda151",
5
5
  "keywords": [
6
6
  "graphql",
7
7
  "relay",
@@ -20,7 +20,7 @@
20
20
  "fbjs": "^3.0.2",
21
21
  "invariant": "^2.2.4",
22
22
  "nullthrows": "^1.1.1",
23
- "relay-runtime": "0.0.0-main-99abbcde"
23
+ "relay-runtime": "0.0.0-main-56cda151"
24
24
  },
25
25
  "peerDependencies": {
26
26
  "react": "^16.9.0 || ^17 || ^18 || ^19"
@@ -16,26 +16,50 @@ import type {
16
16
  PreloadedQueryRef,
17
17
  PreloadedQueryResponse,
18
18
  } from './serverPreloadQuery';
19
- import type {Query, Variables} from 'relay-runtime';
19
+ import type {IEnvironment, Query, Variables} from 'relay-runtime';
20
20
 
21
21
  const usePreloadedQuery = require('../usePreloadedQuery');
22
22
  const useRelayEnvironment = require('../useRelayEnvironment');
23
+ const invariant = require('invariant');
23
24
  // $FlowFixMe[missing-export] React.use is available in React 19+
24
- const {use, useMemo} = require('react');
25
+ const {use, useEffect, useLayoutEffect, useMemo} = require('react');
25
26
  const {
26
- __internal,
27
- ROOT_TYPE,
27
+ Environment: RelayModernEnvironment,
28
28
  createOperationDescriptor,
29
29
  getRequest,
30
30
  } = require('relay-runtime');
31
31
 
32
- const {defaultGetDataID, normalizeResponse} = __internal;
33
-
34
- // $FlowFixMe[unclear-type] WeakSet used for identity-based dedup only
35
- const committedRefs: WeakSet<any> = new WeakSet();
32
+ // Which refs have been published to which environment, and the notify each
33
+ // still owes. Module state, not a component ref: the attempt that publishes
34
+ // need not be the attempt that commits. Keyed by environment because
35
+ // "published" is a fact about one store.
36
+ const publishedQueryRefs: WeakMap<
37
+ IEnvironment,
38
+ // $FlowFixMe[unclear-type] keyed by ref identity only
39
+ WeakMap<any, (() => void) | null>,
40
+ > = new WeakMap();
41
+
42
+ function getPublishedQueryRefs(
43
+ environment: IEnvironment,
44
+ // $FlowFixMe[unclear-type] keyed by ref identity only
45
+ ): WeakMap<any, (() => void) | null> {
46
+ let refs = publishedQueryRefs.get(environment);
47
+ if (refs == null) {
48
+ refs = new WeakMap();
49
+ publishedQueryRefs.set(environment, refs);
50
+ }
51
+ return refs;
52
+ }
36
53
 
37
54
  const DEFAULT_STALE_MS = 30_000;
38
55
 
56
+ // This is a 'use client' hook, so it still renders during SSR, where
57
+ // useLayoutEffect warns and does nothing. There is no notify to flush there
58
+ // either: nothing is subscribed to a server render's store.
59
+ const useIsomorphicLayoutEffect =
60
+ // $FlowFixMe[cannot-resolve-name] `window` has no libdef in the OSS build
61
+ typeof window === 'undefined' ? useEffect : useLayoutEffect;
62
+
39
63
  hook useQueryFromServer<TVariables extends Variables, TData>(
40
64
  query: Query<TVariables, TData>,
41
65
  queryRef: PreloadedQueryRef<TVariables, TData>,
@@ -44,26 +68,25 @@ hook useQueryFromServer<TVariables extends Variables, TData>(
44
68
  const environment = useRelayEnvironment();
45
69
  const request = getRequest(query);
46
70
  const threshold = options?.staleThresholdMs ?? DEFAULT_STALE_MS;
47
- // TODO: Add a method to IEnvironment for server-side publish so custom
48
- // IEnvironment implementations don't need to access _getDataID.
49
- // $FlowFixMe[prop-missing] _getDataID is not on IEnvironment
50
- // $FlowFixMe[unclear-type]
51
- const getDataID: any = environment._getDataID ?? defaultGetDataID;
52
71
 
53
72
  const response: PreloadedQueryResponse<TData> = use(queryRef._response);
54
73
 
55
74
  const isFresh =
56
75
  response.data != null && Date.now() - queryRef.fetchedAt <= threshold;
57
76
 
58
- const shouldCommit = isFresh && !committedRefs.has(queryRef);
77
+ // `publishWithDeferredNotify` is on RelayModernEnvironment rather than
78
+ // IEnvironment while the API is experimental.
79
+ invariant(
80
+ environment instanceof RelayModernEnvironment,
81
+ 'useQueryFromServer: expected a RelayModernEnvironment, got `%s`. ' +
82
+ 'useQueryFromServer is currently only compatible with RelayModernStore.',
83
+ environment.constructor?.name ?? typeof environment,
84
+ );
59
85
 
60
- // Publish server data to the Relay store without notifying subscribers.
61
- // This avoids the React "Cannot update a component while rendering a
62
- // different component" error that occurs when store.notify() triggers
63
- // setState in other mounted components that subscribe to overlapping records.
64
- if (shouldCommit) {
65
- committedRefs.add(queryRef);
86
+ const publishedRefs = getPublishedQueryRefs(environment);
87
+ const shouldCommit = isFresh && !publishedRefs.has(queryRef);
66
88
 
89
+ if (shouldCommit) {
67
90
  const operation = createOperationDescriptor(request, queryRef.variables);
68
91
 
69
92
  // $FlowFixMe[unclear-type]
@@ -71,25 +94,28 @@ hook useQueryFromServer<TVariables extends Variables, TData>(
71
94
  data: response.data,
72
95
  errors: response.errors,
73
96
  };
74
- const relayPayload = normalizeResponse(
75
- responsePayload,
76
- operation.root,
77
- ROOT_TYPE,
78
- {
79
- getDataID,
80
- treatMissingFieldsAsNull: false,
81
- deferDeduplicatedFields: false,
82
- // $FlowFixMe[prop-missing]
83
- log: environment.__log ?? null,
84
- path: [],
85
- shouldProcessClientComponents: false,
86
- },
87
- false,
97
+ publishedRefs.set(
98
+ queryRef,
99
+ environment.publishWithDeferredNotify(operation, responsePayload),
88
100
  );
89
-
90
- environment.getStore().publish(relayPayload.source);
91
101
  }
92
102
 
103
+ // A layout effect rather than during render, which would be a setState in
104
+ // another component mid-render, and rather than a microtask, which can land
105
+ // between concurrent-render work units. Not airtight: a subtree that renders
106
+ // but never mounts never runs this, and its notify waits for the next one.
107
+ useIsomorphicLayoutEffect(() => {
108
+ const refs = getPublishedQueryRefs(environment);
109
+ const flushNotify = refs.get(queryRef);
110
+ if (flushNotify != null) {
111
+ // Null rather than delete: the entry is also what marks this ref
112
+ // published, so removing it would republish on the next render and flip
113
+ // the fetch policy below back to network-only.
114
+ refs.set(queryRef, null);
115
+ flushNotify();
116
+ }
117
+ }, [environment, queryRef]);
118
+
93
119
  // Build a PreloadedQuery shim. Fresh data was committed to the store
94
120
  // above, so source is null and fetchPolicy is "store-or-network".
95
121
  // Stale path uses "network-only" to trigger a client-side refetch.
@@ -101,7 +127,8 @@ hook useQueryFromServer<TVariables extends Variables, TData>(
101
127
  // component instance, read from the store even if the server timestamp
102
128
  // is past the staleness threshold. This prevents a network refetch
103
129
  // from overwriting store mutations made after the initial commit.
104
- const useStore = isFreshAtMemo || committedRefs.has(queryRef);
130
+ const useStore =
131
+ isFreshAtMemo || getPublishedQueryRefs(environment).has(queryRef);
105
132
 
106
133
  return {
107
134
  kind: 'PreloadedQuery_DEPRECATED',
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Relay v0.0.0-main-99abbcde
2
+ * Relay v0.0.0-main-56cda151
3
3
  *
4
4
  * Copyright (c) Meta Platforms, Inc. and affiliates.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Relay v0.0.0-main-99abbcde
2
+ * Relay v0.0.0-main-56cda151
3
3
  *
4
4
  * Copyright (c) Meta Platforms, Inc. and affiliates.
5
5
  *