Skip to content

Commit

Permalink
Patch bind to forward bound arguments to another server reference
Browse files Browse the repository at this point in the history
This is not needed on the client since just regular bind works there.
  • Loading branch information
sebmarkbage committed Feb 8, 2023
1 parent 9b442d0 commit c05caee
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,23 @@ 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;
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<Function>) {
switch (name) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
<ClientRef action={ServerModule.greet.bind(null, 'Hello', 'World')} />,
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(<App />);
});
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!');
});
});

0 comments on commit c05caee

Please sign in to comment.