@@ -63,10 +63,14 @@ export type EncodeFormActionCallback = <A>(
6363
6464export type ServerReferenceId = any ;
6565
66- const knownServerReferences : WeakMap <
67- Function ,
68- { id : ServerReferenceId , bound : null | Thenable < Array < any >> } ,
69- > = new WeakMap ( ) ;
66+ type ServerReferenceClosure = {
67+ id : ServerReferenceId ,
68+ originalBind : Function ,
69+ bound : null | Thenable < Array < any >> ,
70+ } ;
71+
72+ const knownServerReferences: WeakMap< Function , ServerReferenceClosure > =
73+ new WeakMap();
7074
7175// Serializable values
7276export type ReactServerValue =
@@ -760,16 +764,17 @@ export function processReply(
760764 }
761765
762766 if (typeof value === 'function') {
763- const metaData = knownServerReferences . get ( value ) ;
764- if ( metaData !== undefined ) {
765- const metaDataJSON = JSON . stringify ( metaData , resolveToJSON ) ;
767+ const referenceClosure = knownServerReferences . get ( value ) ;
768+ if ( referenceClosure !== undefined ) {
769+ const { id, bound} = referenceClosure ;
770+ const referenceClosureJSON = JSON . stringify ( { id, bound} , resolveToJSON ) ;
766771 if ( formData === null ) {
767772 // Upgrade to use FormData to allow us to stream this value.
768773 formData = new FormData ( ) ;
769774 }
770775 // The reference to this function came from the same client so we can pass it back.
771776 const refId = nextPartId ++ ;
772- formData . set ( formFieldPrefix + refId , metaDataJSON ) ;
777+ formData . set ( formFieldPrefix + refId , referenceClosureJSON ) ;
773778 return serializeServerReferenceID ( refId ) ;
774779 }
775780 if ( temporaryReferences !== undefined && key . indexOf ( ':' ) === - 1 ) {
@@ -864,7 +869,7 @@ export function processReply(
864869}
865870
866871const boundCache : WeakMap <
867- { id : ServerReferenceId , bound : null | Thenable < Array < any > > } ,
872+ ServerReferenceClosure ,
868873 Thenable < FormData > ,
869874> = new WeakMap ( ) ;
870875
@@ -905,21 +910,22 @@ function defaultEncodeFormAction(
905910 this : any => Promise < any > ,
906911 identifierPrefix : string ,
907912) : ReactCustomFormAction {
908- const reference = knownServerReferences . get ( this ) ;
909- if ( ! reference ) {
913+ const referenceClosure = knownServerReferences . get ( this ) ;
914+ if ( ! referenceClosure ) {
910915 throw new Error (
911916 'Tried to encode a Server Action from a different instance than the encoder is from. ' +
912917 'This is a bug in React.' ,
913918 ) ;
914919 }
915920 let data : null | FormData = null ;
916921 let name ;
917- const boundPromise = reference . bound ;
922+ const boundPromise = referenceClosure . bound ;
918923 if ( boundPromise !== null ) {
919- let thenable = boundCache . get ( reference ) ;
924+ let thenable = boundCache . get ( referenceClosure ) ;
920925 if ( ! thenable ) {
921- thenable = encodeFormData ( reference ) ;
922- boundCache . set ( reference , thenable ) ;
926+ const { id, bound} = referenceClosure ;
927+ thenable = encodeFormData ( { id, bound} ) ;
928+ boundCache . set ( referenceClosure , thenable ) ;
923929 }
924930 if ( thenable . status === 'rejected' ) {
925931 throw thenable . reason ;
@@ -941,7 +947,7 @@ function defaultEncodeFormAction(
941947 name = '$ACTION_REF_' + identifierPrefix ;
942948 } else {
943949 // This is the simple case so we can just encode the ID.
944- name = '$ACTION_ID_' + reference . id ;
950+ name = '$ACTION_ID_' + referenceClosure . id ;
945951 }
946952 return {
947953 name : name ,
@@ -952,42 +958,42 @@ function defaultEncodeFormAction(
952958}
953959
954960function customEncodeFormAction (
955- proxy : any => Promise < any > ,
961+ reference : any => Promise < any > ,
956962 identifierPrefix : string ,
957963 encodeFormAction : EncodeFormActionCallback ,
958964) : ReactCustomFormAction {
959- const reference = knownServerReferences . get ( proxy ) ;
960- if ( ! reference ) {
965+ const referenceClosure = knownServerReferences . get ( reference ) ;
966+ if ( ! referenceClosure ) {
961967 throw new Error (
962968 'Tried to encode a Server Action from a different instance than the encoder is from. ' +
963969 'This is a bug in React.' ,
964970 ) ;
965971 }
966- let boundPromise : Promise < Array < any >> = ( reference . bound : any ) ;
972+ let boundPromise : Promise < Array < any >> = ( referenceClosure . bound : any ) ;
967973 if ( boundPromise === null ) {
968974 boundPromise = Promise . resolve ( [ ] ) ;
969975 }
970- return encodeFormAction(reference .id, boundPromise);
976+ return encodeFormAction(referenceClosure .id, boundPromise);
971977}
972978
973979function isSignatureEqual (
974980 this : any => Promise < any > ,
975981 referenceId: ServerReferenceId,
976982 numberOfBoundArgs: number,
977983): boolean {
978- const reference = knownServerReferences . get ( this ) ;
979- if ( ! reference ) {
984+ const referenceClosure = knownServerReferences . get ( this ) ;
985+ if ( ! referenceClosure ) {
980986 throw new Error (
981987 'Tried to encode a Server Action from a different instance than the encoder is from. ' +
982988 'This is a bug in React.' ,
983989 ) ;
984990 }
985- if ( reference . id !== referenceId ) {
991+ if ( referenceClosure . id !== referenceId ) {
986992 // These are different functions.
987993 return false ;
988994 }
989995 // Now check if the number of bound arguments is the same.
990- const boundPromise = reference . bound ;
996+ const boundPromise = referenceClosure . bound ;
991997 if ( boundPromise === null ) {
992998 // No bound arguments.
993999 return numberOfBoundArgs === 0 ;
@@ -1131,10 +1137,20 @@ export function registerBoundServerReference<T: Function>(
11311137 bound : null | Thenable < Array < any >> ,
11321138 encodeFormAction : void | EncodeFormActionCallback ,
11331139) : void {
1140+ if ( knownServerReferences . has ( reference ) ) {
1141+ return ;
1142+ }
1143+
1144+ knownServerReferences . set ( reference , {
1145+ id,
1146+ originalBind : reference . bind ,
1147+ bound,
1148+ } ) ;
1149+
11341150 // Expose encoder for use by SSR, as well as a special bind that can be used to
11351151 // keep server capabilities.
11361152 if ( usedWithSSR ) {
1137- // Only expose this in builds that would actually use it. Not needed on the client .
1153+ // Only expose this in builds that would actually use it. Not needed in the browser .
11381154 const $$FORM_ACTION =
11391155 encodeFormAction === undefined
11401156 ? defaultEncodeFormAction
@@ -1154,7 +1170,6 @@ export function registerBoundServerReference<T: Function>(
11541170 bind : { value : bind } ,
11551171 } ) ;
11561172 }
1157- knownServerReferences . set ( reference , { id, bound} ) ;
11581173}
11591174
11601175export function registerServerReference < T : Function > (
@@ -1171,43 +1186,54 @@ const FunctionBind = Function.prototype.bind;
11711186// $FlowFixMe[method-unbinding]
11721187const ArraySlice = Array . prototype . slice ;
11731188function bind ( this : Function ) : Function {
1174- // $FlowFixMe[unsupported-syntax]
1175- // $FlowFixMe[prop-missing]
1176- const newFn = FunctionBind . apply ( this , arguments ) ;
1177- const reference = knownServerReferences . get ( this ) ;
1178- if ( reference ) {
1179- if ( __DEV__ ) {
1180- const thisBind = arguments [ 0 ] ;
1181- if ( thisBind != null ) {
1182- // This doesn't warn in browser environments since it's not instrumented outside
1183- // usedWithSSR. This makes this an SSR only warning which we don't generally do.
1184- // TODO: Consider a DEV only instrumentation in the browser.
1185- console . error (
1186- 'Cannot bind "this" of a Server Action. Pass null or undefined as the first argument to .bind().' ,
1187- ) ;
1188- }
1189- }
1190- const args = ArraySlice . call ( arguments , 1 ) ;
1191- let boundPromise = null ;
1192- if ( reference . bound !== null ) {
1193- boundPromise = Promise . resolve ( ( reference . bound : any ) ) . then ( boundArgs =>
1194- boundArgs . concat ( args ) ,
1189+ const referenceClosure = knownServerReferences . get ( this ) ;
1190+
1191+ if ( ! referenceClosure ) {
1192+ // $FlowFixMe[prop-missing]
1193+ return FunctionBind . apply ( this , arguments ) ;
1194+ }
1195+
1196+ const newFn = referenceClosure . originalBind . apply ( this , arguments ) ;
1197+
1198+ if ( __DEV__ ) {
1199+ const thisBind = arguments [ 0 ] ;
1200+ if ( thisBind != null ) {
1201+ // This doesn't warn in browser environments since it's not instrumented outside
1202+ // usedWithSSR. This makes this an SSR only warning which we don't generally do.
1203+ // TODO: Consider a DEV only instrumentation in the browser.
1204+ console . error (
1205+ 'Cannot bind "this" of a Server Action. Pass null or undefined as the first argument to .bind().' ,
11951206 ) ;
1196- } else {
1197- boundPromise = Promise . resolve ( args ) ;
1198- }
1199- // Expose encoder for use by SSR, as well as a special bind that can be used to
1200- // keep server capabilities.
1201- if ( usedWithSSR ) {
1202- // Only expose this in builds that would actually use it. Not needed on the client.
1203- Object . defineProperties ( ( newFn : any ) , {
1204- $$FORM_ACTION : { value : this . $$FORM_ACTION } ,
1205- $$IS_SIGNATURE_EQUAL : { value : isSignatureEqual } ,
1206- bind : { value : bind } ,
1207- } ) ;
12081207 }
1209- knownServerReferences . set ( newFn , { id : reference . id , bound : boundPromise } ) ;
12101208 }
1209+
1210+ const args = ArraySlice . call ( arguments , 1 ) ;
1211+ let boundPromise = null ;
1212+ if ( referenceClosure . bound !== null ) {
1213+ boundPromise = Promise . resolve ( ( referenceClosure . bound : any ) ) . then (
1214+ boundArgs => boundArgs . concat ( args ) ,
1215+ ) ;
1216+ } else {
1217+ boundPromise = Promise . resolve ( args ) ;
1218+ }
1219+
1220+ knownServerReferences . set ( newFn , {
1221+ id : referenceClosure . id ,
1222+ originalBind : newFn . bind ,
1223+ bound : boundPromise ,
1224+ } ) ;
1225+
1226+ // Expose encoder for use by SSR, as well as a special bind that can be used to
1227+ // keep server capabilities.
1228+ if ( usedWithSSR ) {
1229+ // Only expose this in builds that would actually use it. Not needed on the client.
1230+ Object . defineProperties ( ( newFn : any ) , {
1231+ $$FORM_ACTION : { value : this . $$FORM_ACTION } ,
1232+ $$IS_SIGNATURE_EQUAL : { value : isSignatureEqual } ,
1233+ bind : { value : bind } ,
1234+ } ) ;
1235+ }
1236+
12111237 return newFn ;
12121238}
12131239
0 commit comments