Skip to content

Commit c05caee

Browse files
committed
Patch bind to forward bound arguments to another server reference
This is not needed on the client since just regular bind works there.
1 parent 9b442d0 commit c05caee

2 files changed

Lines changed: 68 additions & 0 deletions

File tree

packages/react-server-dom-webpack/src/ReactFlightWebpackNodeRegister.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,23 @@ module.exports = function register() {
1818
const SERVER_REFERENCE = Symbol.for('react.server.reference');
1919
const PROMISE_PROTOTYPE = Promise.prototype;
2020

21+
// Patch bind on the server to ensure that this creates another
22+
// bound server reference with the additional arguments.
23+
const originalBind = Function.prototype.bind;
24+
Function.prototype.bind = (function bind(this: any, self: any) {
25+
// $FlowFixMe[unsupported-syntax]
26+
const newFn = originalBind.apply(this, arguments);
27+
if (this.$$typeof === SERVER_REFERENCE) {
28+
// $FlowFixMe[method-unbinding]
29+
const args = Array.prototype.slice.call(arguments, 1);
30+
newFn.$$typeof = SERVER_REFERENCE;
31+
newFn.$$filepath = this.$$filepath;
32+
newFn.$$name = this.$$name;
33+
newFn.$$bound = this.$$bound.concat(args);
34+
}
35+
return newFn;
36+
}: any);
37+
2138
const deepProxyHandlers = {
2239
get: function (target: Function, name: string, receiver: Proxy<Function>) {
2340
switch (name) {

packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -870,4 +870,55 @@ describe('ReactFlightDOMBrowser', () => {
870870
const result = await actionProxy('hi');
871871
expect(result).toBe('HI');
872872
});
873+
874+
it('can bind arguments to a server reference', async () => {
875+
let actionProxy;
876+
877+
function Client({action}) {
878+
actionProxy = action;
879+
return 'Click Me';
880+
}
881+
882+
function greet(a, b, c) {
883+
return a + ' ' + b + c;
884+
}
885+
886+
const ServerModule = serverExports({
887+
greet,
888+
});
889+
const ClientRef = clientExports(Client);
890+
891+
const stream = ReactServerDOMWriter.renderToReadableStream(
892+
<ClientRef action={ServerModule.greet.bind(null, 'Hello', 'World')} />,
893+
webpackMap,
894+
);
895+
896+
function requireServerRef(ref) {
897+
const metaData = webpackServerMap[ref.id][ref.name];
898+
return __webpack_require__(metaData.id)[metaData.name];
899+
}
900+
901+
const response = ReactServerDOMReader.createFromReadableStream(stream, {
902+
async callServer(ref, args) {
903+
const fn = requireServerRef(ref);
904+
return fn.apply(null, args);
905+
},
906+
});
907+
908+
function App() {
909+
return use(response);
910+
}
911+
912+
const container = document.createElement('div');
913+
const root = ReactDOMClient.createRoot(container);
914+
await act(async () => {
915+
root.render(<App />);
916+
});
917+
expect(container.innerHTML).toBe('Click Me');
918+
expect(typeof actionProxy).toBe('function');
919+
expect(actionProxy).not.toBe(greet);
920+
921+
const result = await actionProxy('!');
922+
expect(result).toBe('Hello World!');
923+
});
873924
});

0 commit comments

Comments
 (0)