Skip to content

Commit

Permalink
[Flight] Warn if this argument is passed to .bind of a Server Referen…
Browse files Browse the repository at this point in the history
…ce (#28380)

This won't ever be serialized and is likely just a mistake.

This should be covered by the "use server" compiler since it ensures
that something that accepts a "this" won't be allowed to compile and if
it doesn't accept it, TypeScript should ideally forbid it to be passed.

So maybe this is unnecessary.
  • Loading branch information
sebmarkbage authored Feb 19, 2024
1 parent 017397d commit 65a0e2b
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 0 deletions.
11 changes: 11 additions & 0 deletions packages/react-client/src/ReactFlightReplyClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,17 @@ function bind(this: Function): Function {
const newFn = FunctionBind.apply(this, arguments);
const reference = knownServerReferences.get(this);
if (reference) {
if (__DEV__) {
const thisBind = arguments[0];
if (thisBind != null) {
// This doesn't warn in browser environments since it's not instrumented outside
// usedWithSSR. This makes this an SSR only warning which we don't generally do.
// TODO: Consider a DEV only instrumentation in the browser.
console.error(
'Cannot bind "this" of a Server Action. Pass null or undefined as the first argument to .bind().',
);
}
}
const args = ArraySlice.call(arguments, 1);
let boundPromise = null;
if (reference.bound !== null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@ function bind(this: ServerReference<any>): any {
// $FlowFixMe[unsupported-syntax]
const newFn = FunctionBind.apply(this, arguments);
if (this.$$typeof === SERVER_REFERENCE_TAG) {
if (__DEV__) {
const thisBind = arguments[0];
if (thisBind != null) {
console.error(
'Cannot bind "this" of a Server Action. Pass null or undefined as the first argument to .bind().',
);
}
}
const args = ArraySlice.call(arguments, 1);
return Object.defineProperties((newFn: any), {
$$typeof: {value: SERVER_REFERENCE_TAG},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ global.TextDecoder = require('util').TextDecoder;
// TODO: we can replace this with FlightServer.act().
global.setTimeout = cb => cb();

let serverExports;
let clientExports;
let webpackMap;
let webpackModules;
Expand All @@ -41,6 +42,7 @@ describe('ReactFlightDOMEdge', () => {

const WebpackMock = require('./utils/WebpackMock');

serverExports = WebpackMock.serverExports;
clientExports = WebpackMock.clientExports;
webpackMap = WebpackMock.webpackMap;
webpackModules = WebpackMock.webpackModules;
Expand Down Expand Up @@ -323,4 +325,31 @@ describe('ReactFlightDOMEdge', () => {
});
expect(result).toEqual(buffers);
});

it('warns if passing a this argument to bind() of a server reference', async () => {
const ServerModule = serverExports({
greet: function () {},
});

const ServerModuleImportedOnClient = {
greet: ReactServerDOMClient.createServerReference(
ServerModule.greet.$$id,
async function (ref, args) {},
),
};

expect(() => {
ServerModule.greet.bind({}, 'hi');
}).toErrorDev(
'Cannot bind "this" of a Server Action. Pass null or undefined as the first argument to .bind().',
{withoutStack: true},
);

expect(() => {
ServerModuleImportedOnClient.greet.bind({}, 'hi');
}).toErrorDev(
'Cannot bind "this" of a Server Action. Pass null or undefined as the first argument to .bind().',
{withoutStack: true},
);
});
});

0 comments on commit 65a0e2b

Please sign in to comment.