-
Notifications
You must be signed in to change notification settings - Fork 47.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Flight Reply] Encode Objects Returned to the Client by Reference #29010
Conversation
The size diff is too large to display in a single comment. The CircleCI job contains an artifact called 'sizebot-message.md' with the full message. |
5f5f445
to
b1045f1
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume you didn't add tests for the behavior of references with :
in it because we really should support it with escaping and that will change your assertions but I think there are probably some failure modes for those property names that are going to be really confusing if anyone uses this with colons.
But I spent like 5 minutes thinking about the escaping and it seems non-trivial so I guess I get why it's not in this PR.
Are you going to follow up with that imminently?
if (parentReference !== undefined) { | ||
// If the parent has a reference, we can refer to this object indirectly | ||
// through the property name inside that parent. | ||
const reference = parentReference + ':' + key; | ||
// Store this object so that the server can refer to it later in responses. | ||
writeTemporaryReference(temporaryReferences, reference, value); | ||
return serializeTemporaryReferenceMarker(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
struggling with the constraints here.
When would parentReference be undefined? Presumably never because otherwise you'd fall through the error below which doesn't really explain the case you are hitting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea it would only be in the case of a bug in React I think.
@@ -247,6 +270,7 @@ function resolveModelChunk<T>(chunk: SomeChunk<T>, value: string): void { | |||
const resolvedChunk: ResolvedModelChunk<T> = (chunk: any); | |||
resolvedChunk.status = RESOLVED_MODEL; | |||
resolvedChunk.value = value; | |||
resolvedChunk.reason = id; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wish we could give these properties virtual names. so hard to keep track of what they do in every context you use them
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose we could give them helpers like setChunkID(ResolvedModelChunk, number)
but I don't find it that big of a deal since it's only in the init case and then when you read it and in each case the variable shows what the alias means when reading the code.
It's not directly related to this PR but the previous ones. In particular, if you use a I'm not going to do it imminently fix it. It's an exercise left for the reader. |
This lets us also register objects that are not opaque by reference.
This ensures that objects can only be passed by reference if they're part of the same pair of renders. This also ensures that you can avoid this short cut when it's not possible such as in SSR passes.
Stacked on #28997.
We can use the technique of referencing an object by its row + property name path for temporary references - like we do for deduping. That way we don't need to generate an ID for temporary references. Instead, they can just be an opaque marker in the slot and it has the implicit ID of the row + path.
Then we can stash all objects, even the ones that are actually available to read on the server, as temporary references. Without adding anything to the payload since the IDs are implicit. If the same object is returned to the client, it can be referenced by reference instead of serializing it back to the client. This also helps preserve object identity.
We assume that the objects are immutable when they pass the boundary.
I'm not sure if this is worth it but with this mechanism, if you return the
FormData
payload from auseActionState
it doesn't have to be serialized on the way back to the client. This is a common pattern for having access to the last submission as "default value" to the form fields. However you can still control it by replacing it with another object if you want. In MPA mode, the temporary references are not configured and so it needs to be serialized in that case. That's required anyway for hydration purposes.I'm not sure if people will actually use this in practice though or if FormData will always be destructured into some other object like with a library that turns it into typed data, and back. If so, the object identity is lost.