Skip to content

Commit 384fb81

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 8ac3762 commit 384fb81

File tree

2 files changed

+69
-5
lines changed

2 files changed

+69
-5
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 & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -816,6 +816,15 @@ describe('ReactFlightDOMBrowser', () => {
816816
expect(container.innerHTML).toBe('Hi');
817817
});
818818

819+
function requireServerRef(ref) {
820+
const metaData = webpackServerMap[ref.id][ref.name];
821+
const mod = __webpack_require__(metaData.id);
822+
if (metaData.name === '*') {
823+
return mod;
824+
}
825+
return mod[metaData.name];
826+
}
827+
819828
it('can pass a function by reference from server to client', async () => {
820829
let actionProxy;
821830

@@ -838,11 +847,6 @@ describe('ReactFlightDOMBrowser', () => {
838847
webpackMap,
839848
);
840849

841-
function requireServerRef(ref) {
842-
const metaData = webpackServerMap[ref.id][ref.name];
843-
return __webpack_require__(metaData.id)[metaData.name];
844-
}
845-
846850
const response = ReactServerDOMReader.createFromReadableStream(stream, {
847851
async callServer(ref, args) {
848852
const fn = requireServerRef(ref);
@@ -866,4 +870,46 @@ describe('ReactFlightDOMBrowser', () => {
866870
const result = await actionProxy('hi');
867871
expect(result).toBe('HI');
868872
});
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+
const greet = serverExports(function greet(a, b, c) {
883+
return a + ' ' + b + c;
884+
});
885+
const ClientRef = clientExports(Client);
886+
887+
const stream = ReactServerDOMWriter.renderToReadableStream(
888+
<ClientRef action={greet.bind(null, 'Hello', 'World')} />,
889+
webpackMap,
890+
);
891+
892+
const response = ReactServerDOMReader.createFromReadableStream(stream, {
893+
async callServer(ref, args) {
894+
const fn = requireServerRef(ref);
895+
return fn.apply(null, args);
896+
},
897+
});
898+
899+
function App() {
900+
return use(response);
901+
}
902+
903+
const container = document.createElement('div');
904+
const root = ReactDOMClient.createRoot(container);
905+
await act(async () => {
906+
root.render(<App />);
907+
});
908+
expect(container.innerHTML).toBe('Click Me');
909+
expect(typeof actionProxy).toBe('function');
910+
expect(actionProxy).not.toBe(greet);
911+
912+
const result = await actionProxy('!');
913+
expect(result).toBe('Hello World!');
914+
});
869915
});

0 commit comments

Comments
 (0)