Skip to content

Commit b2c90c0

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 b2c90c0

File tree

2 files changed

+69
-0
lines changed

2 files changed

+69
-0
lines changed

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,24 @@ 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+
/*eslint-disable no-extend-native */
25+
Function.prototype.bind = (function bind(this: any, self: any) {
26+
// $FlowFixMe[unsupported-syntax]
27+
const newFn = originalBind.apply(this, arguments);
28+
if (this.$$typeof === SERVER_REFERENCE) {
29+
// $FlowFixMe[method-unbinding]
30+
const args = Array.prototype.slice.call(arguments, 1);
31+
newFn.$$typeof = SERVER_REFERENCE;
32+
newFn.$$filepath = this.$$filepath;
33+
newFn.$$name = this.$$name;
34+
newFn.$$bound = this.$$bound.concat(args);
35+
}
36+
return newFn;
37+
}: any);
38+
2139
const deepProxyHandlers = {
2240
get: function (target: Function, name: string, receiver: Proxy<Function>) {
2341
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)