diff --git a/packages/react-server-dom-webpack/src/ReactFlightWebpackNodeRegister.js b/packages/react-server-dom-webpack/src/ReactFlightWebpackNodeRegister.js index bb825940f0087..eef4c5da9fff8 100644 --- a/packages/react-server-dom-webpack/src/ReactFlightWebpackNodeRegister.js +++ b/packages/react-server-dom-webpack/src/ReactFlightWebpackNodeRegister.js @@ -18,6 +18,24 @@ module.exports = function register() { const SERVER_REFERENCE = Symbol.for('react.server.reference'); const PROMISE_PROTOTYPE = Promise.prototype; + // Patch bind on the server to ensure that this creates another + // bound server reference with the additional arguments. + const originalBind = Function.prototype.bind; + /*eslint-disable no-extend-native */ + Function.prototype.bind = (function bind(this: any, self: any) { + // $FlowFixMe[unsupported-syntax] + const newFn = originalBind.apply(this, arguments); + if (this.$$typeof === SERVER_REFERENCE) { + // $FlowFixMe[method-unbinding] + const args = Array.prototype.slice.call(arguments, 1); + newFn.$$typeof = SERVER_REFERENCE; + newFn.$$filepath = this.$$filepath; + newFn.$$name = this.$$name; + newFn.$$bound = this.$$bound.concat(args); + } + return newFn; + }: any); + const deepProxyHandlers = { get: function (target: Function, name: string, receiver: Proxy) { switch (name) { diff --git a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js index 30429e06db92b..306d8c5a3487d 100644 --- a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js +++ b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js @@ -870,4 +870,55 @@ describe('ReactFlightDOMBrowser', () => { const result = await actionProxy('hi'); expect(result).toBe('HI'); }); + + it('can bind arguments to a server reference', async () => { + let actionProxy; + + function Client({action}) { + actionProxy = action; + return 'Click Me'; + } + + function greet(a, b, c) { + return a + ' ' + b + c; + } + + const ServerModule = serverExports({ + greet, + }); + const ClientRef = clientExports(Client); + + const stream = ReactServerDOMWriter.renderToReadableStream( + , + webpackMap, + ); + + function requireServerRef(ref) { + const metaData = webpackServerMap[ref.id][ref.name]; + return __webpack_require__(metaData.id)[metaData.name]; + } + + const response = ReactServerDOMReader.createFromReadableStream(stream, { + async callServer(ref, args) { + const fn = requireServerRef(ref); + return fn.apply(null, args); + }, + }); + + function App() { + return use(response); + } + + const container = document.createElement('div'); + const root = ReactDOMClient.createRoot(container); + await act(async () => { + root.render(); + }); + expect(container.innerHTML).toBe('Click Me'); + expect(typeof actionProxy).toBe('function'); + expect(actionProxy).not.toBe(greet); + + const result = await actionProxy('!'); + expect(result).toBe('Hello World!'); + }); });