@@ -2,20 +2,16 @@ import type {
22 GraphQLResponse ,
33 GraphQLTaggedNode ,
44 Subscribable ,
5+ Subscription ,
56} from "relay-runtime" ;
67import { observeFragment } from "relay-runtime/experimental.js" ;
7- import {
8- batch ,
9- createComputed ,
10- createMemo ,
11- createResource ,
12- onCleanup ,
13- } from "solid-js" ;
8+ import { batch , createResource , createSignal , untrack } from "solid-js" ;
149import type { Accessor } from "solid-js" ;
15- import { createStore , reconcile , unwrap } from "solid-js/store" ;
10+ import { type SetStoreFunction , reconcile , unwrap } from "solid-js/store" ;
11+ import { isServer } from "solid-js/web" ;
1612import { useRelayEnvironment } from "../RelayEnvironment" ;
1713import type { KeyType , KeyTypeData } from "../types/keyType" ;
18- import { type DataProxy , makeDataProxy } from "../utils/dataProxy " ;
14+ import { type DataStore , createDataStore } from "../utils/dataStore " ;
1915
2016type FragmentResult < T > =
2117 | {
@@ -37,15 +33,15 @@ type FragmentResult<T> =
3733export function createFragment < TKey extends KeyType > (
3834 fragment : GraphQLTaggedNode ,
3935 key : Accessor < TKey > ,
40- ) : DataProxy < KeyTypeData < TKey > > ;
36+ ) : DataStore < KeyTypeData < TKey > > ;
4137export function createFragment < TKey extends KeyType > (
4238 fragment : GraphQLTaggedNode ,
4339 key : Accessor < TKey | null | undefined > ,
44- ) : DataProxy < KeyTypeData < TKey > | null | undefined > ;
40+ ) : DataStore < KeyTypeData < TKey > | null | undefined > ;
4541export function createFragment < TKey extends KeyType > (
4642 fragment : GraphQLTaggedNode ,
4743 key : Accessor < TKey | null | undefined > ,
48- ) : DataProxy < KeyTypeData < TKey > | null | undefined > {
44+ ) : DataStore < KeyTypeData < TKey > | null | undefined > {
4945 return createFragmentInternal ( fragment , key ) ;
5046}
5147
@@ -55,21 +51,67 @@ export function createFragmentInternal<TKey extends KeyType>(
5551 options ?: Accessor < {
5652 parentOperation : Subscribable < GraphQLResponse > | null | undefined ;
5753 } > ,
58- ) : DataProxy < KeyTypeData < TKey > | null | undefined > {
54+ ) : DataStore < KeyTypeData < TKey > | null | undefined > {
5955 const environment = useRelayEnvironment ( ) ;
6056
61- const source = createMemo ( ( ) => {
62- const k = unwrap ( key ( ) ) ;
63- return k && observeFragment ( environment ( ) , fragment , k ) ;
64- } ) ;
57+ const initialResult : FragmentResult < TKey [ " $data" ] > = {
58+ data : undefined ,
59+ error : undefined ,
60+ pending : false ,
61+ } ;
62+
63+ type FragmentObserver = Parameters <
64+ ReturnType < typeof observeFragment > [ "subscribe" ]
65+ > [ 0 ] ;
66+ const resultUpdateObserver = {
67+ next ( res ) {
68+ batch ( ( ) => {
69+ switch ( res . state ) {
70+ case "ok" :
71+ setResult ( "error" , undefined ) ;
72+ setResult ( "pending" , false ) ;
73+ setResult (
74+ "data" ,
75+ reconcile ( res . value as Record < string , unknown > , {
76+ key : "__id" ,
77+ merge : true ,
78+ } ) ,
79+ ) ;
80+ break ;
81+ case "error" :
82+ setResult ( "data" , undefined ) ;
83+ setResult ( "error" , res . error ) ;
84+ setResult ( "pending" , false ) ;
85+ break ;
86+ }
87+ } ) ;
88+ } ,
89+ } satisfies FragmentObserver ;
90+ const [ subscription , setSubscription ] = createSignal < Subscription > ( ) ;
91+
92+ const setResultQueue : unknown [ ] [ ] = [ ] ;
93+ let setResult : SetStoreFunction < FragmentResult < TKey [ " $data" ] > > = (
94+ ...args : unknown [ ]
95+ ) => {
96+ setResultQueue . push ( args ) ;
97+ } ;
6598
6699 const [ resource ] = createResource (
67100 ( ) => {
68- const s = source ( ) ;
69- if ( ! s ) return ;
70- return { source : s , parentOperation : options ?.( ) . parentOperation } ;
101+ batch ( ( ) => {
102+ untrack ( subscription ) ?. unsubscribe ( ) ;
103+ setSubscription ( undefined ) ;
104+ setResult ( initialResult ) ;
105+ } ) ;
106+
107+ void environment ( ) ;
108+ const k = unwrap ( key ( ) ) ;
109+ if ( ! k ) return ;
110+ return { key : k , parentOperation : options ?.( ) . parentOperation } ;
71111 } ,
72- async ( { source, parentOperation } ) => {
112+ async ( { key, parentOperation } ) => {
113+ setResult ( "pending" , true ) ;
114+
73115 if ( parentOperation ) {
74116 await new Promise < void > ( ( resolve , reject ) => {
75117 parentOperation . subscribe ( {
@@ -79,66 +121,45 @@ export function createFragmentInternal<TKey extends KeyType>(
79121 } ) ;
80122 }
81123
124+ const source = observeFragment ( environment ( ) , fragment , key ) ;
125+
82126 return new Promise < true > ( ( resolve , reject ) => {
83- const subscription = source . subscribe ( {
84- next ( value ) {
85- if ( value . state === "ok" ) {
86- resolve ( true ) ;
87- queueMicrotask ( ( ) => subscription . unsubscribe ( ) ) ;
88- } else if ( value . state === "error" ) {
89- reject ( value . error ) ;
90- queueMicrotask ( ( ) => subscription . unsubscribe ( ) ) ;
91- }
92- } ,
93- } ) ;
127+ setSubscription (
128+ source . subscribe ( {
129+ next ( res ) {
130+ resultUpdateObserver . next ( res ) ;
131+ if ( res . state === "ok" ) resolve ( true ) ;
132+ else if ( res . state === "error" ) reject ( res . error ) ;
133+ } ,
134+ } ) ,
135+ ) ;
136+ } ) . finally ( ( ) => {
137+ if ( isServer ) {
138+ subscription ( ) ?. unsubscribe ( ) ;
139+ setSubscription ( undefined ) ;
140+ }
94141 } ) ;
95142 } ,
96- ) ;
97-
98- const initialResult : FragmentResult < TKey [ " $data" ] > = {
99- data : undefined ,
100- error : undefined ,
101- pending : false ,
102- } ;
103- const [ result , setResult ] =
104- createStore < FragmentResult < TKey [ " $data" ] > > ( initialResult ) ;
105-
106- createComputed ( ( ) => {
107- setResult ( initialResult ) ;
108- const currentSource = source ( ) ;
109- if ( ! currentSource ) return ;
110-
111- setResult ( "pending" , true ) ;
112-
113- const subscription = currentSource . subscribe ( {
114- next ( res ) {
115- batch ( ( ) => {
116- switch ( res . state ) {
117- case "ok" :
118- setResult ( "error" , undefined ) ;
119- setResult ( "pending" , false ) ;
120- setResult (
121- "data" ,
122- reconcile ( res . value as Record < string , unknown > , {
123- key : "__id" ,
124- merge : true ,
125- } ) ,
126- ) ;
127- break ;
128- case "error" :
129- setResult ( "data" , undefined ) ;
130- setResult ( "error" , res . error ) ;
131- setResult ( "pending" , false ) ;
132- break ;
133- }
134- } ) ;
143+ {
144+ onHydrated ( source ) {
145+ if ( ! source ) return ;
146+ setSubscription (
147+ observeFragment ( environment ( ) , fragment , source . key ) . subscribe (
148+ resultUpdateObserver ,
149+ ) ,
150+ ) ;
135151 } ,
136- } ) ;
152+ } ,
153+ ) ;
137154
138- onCleanup ( ( ) => {
139- subscription . unsubscribe ( ) ;
140- } ) ;
141- } ) ;
155+ const store = createDataStore < FragmentResult < TKey [ " $data" ] > > (
156+ initialResult ,
157+ resource ,
158+ ) ;
159+ for ( const args of setResultQueue ) {
160+ store [ 1 ] . apply ( undefined , args as never ) ;
161+ }
162+ setResult = store [ 1 ] ;
142163
143- return makeDataProxy ( result , resource ) ;
164+ return store [ 0 ] ;
144165}
0 commit comments