Skip to content

Commit 3c2ebde

Browse files
committed
Error if refs are passed in server components or to client components
1 parent 6f6f032 commit 3c2ebde

File tree

3 files changed

+28
-8
lines changed

3 files changed

+28
-8
lines changed

packages/react-client/src/__tests__/ReactFlight-test.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,9 +165,15 @@ describe('ReactFlight', () => {
165165
return <div foo={Symbol('foo')} />;
166166
}
167167

168+
const ref = React.createRef();
169+
function RefProp() {
170+
return <div ref={ref} />;
171+
}
172+
168173
const event = ReactNoopFlightServer.render(<EventHandlerProp />);
169174
const fn = ReactNoopFlightServer.render(<FunctionProp />);
170175
const symbol = ReactNoopFlightServer.render(<SymbolProp />);
176+
const refs = ReactNoopFlightServer.render(<RefProp />);
171177

172178
function Client({transport}) {
173179
return ReactNoopFlightClient.read(transport);
@@ -185,6 +191,9 @@ describe('ReactFlight', () => {
185191
<ErrorBoundary expectedMessage="Symbol values (foo) cannot be passed to client components.">
186192
<Client transport={symbol} />
187193
</ErrorBoundary>
194+
<ErrorBoundary expectedMessage="Refs cannot be passed from server components to client components.">
195+
<Client transport={refs} />
196+
</ErrorBoundary>
188197
</>,
189198
);
190199
});
@@ -217,10 +226,8 @@ describe('ReactFlight', () => {
217226
);
218227
});
219228

220-
it('should NOT warn in DEV for key/ref getters', () => {
221-
const transport = ReactNoopFlightServer.render(
222-
<div key="a" ref={() => {}} />,
223-
);
229+
it('should NOT warn in DEV for key getters', () => {
230+
const transport = ReactNoopFlightServer.render(<div key="a" />);
224231
act(() => {
225232
ReactNoop.render(ReactNoopFlightClient.read(transport));
226233
});

packages/react-server/src/ReactFlightServer.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,15 @@ export function createRequest(
119119
function attemptResolveElement(element: React$Element<any>): ReactModel {
120120
const type = element.type;
121121
const props = element.props;
122+
if (element.ref !== null && element.ref !== undefined) {
123+
// When the ref moves to the regular props object this will implicitly
124+
// throw for functions. We could probably relax it to a DEV warning for other
125+
// cases.
126+
invariant(
127+
false,
128+
'Refs cannot be used in server components, nor passed to client components.',
129+
);
130+
}
122131
if (typeof type === 'function') {
123132
// This is a server-side component.
124133
return type(props);
@@ -153,7 +162,7 @@ function attemptResolveElement(element: React$Element<any>): ReactModel {
153162
}
154163
}
155164
}
156-
invariant(false, 'Unsupported type.');
165+
invariant(false, 'Unsupported server component type: %s', describeValueForErrorMessage(type));
157166
}
158167

159168
function pingSegment(request: Request, segment: Segment): void {
@@ -222,7 +231,10 @@ function isSimpleObject(object): boolean {
222231
return false;
223232
}
224233
if (!descriptor.enumerable) {
225-
if ((names[i] === 'key' || names[i] === 'ref') && typeof descriptor.get === 'function') {
234+
if (
235+
(names[i] === 'key' || names[i] === 'ref') &&
236+
typeof descriptor.get === 'function'
237+
) {
226238
// React adds key and ref getters to props objects to issue warnings.
227239
// Those getters will not be transferred to the client, but that's ok,
228240
// so we'll special case them.

scripts/error-codes/codes.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@
340340
"348": "ensureListeningTo(): received a container that was not an element node. This is likely a bug in React.",
341341
"349": "Expected a work-in-progress root. This is a bug in React. Please file an issue.",
342342
"350": "Cannot read from mutable source during the current render without tearing. This is a bug in React. Please file an issue.",
343-
"351": "Unsupported type.",
343+
"351": "Unsupported server component type: %s",
344344
"352": "React Blocks (and Lazy Components) are expected to be replaced by a compiler on the server. Try configuring your compiler set up and avoid using React.lazy inside of Blocks.",
345345
"353": "A server block should never encode any other slots. This is a bug in React.",
346346
"354": "getInspectorDataForViewAtPoint() is not available in production.",
@@ -366,5 +366,6 @@
366366
"375": "Functions cannot be passed directly to client components because they're not serializable. Remove %s (%s) from this object, or avoid the entire object: %s",
367367
"376": "Symbol values (%s) cannot be passed to client components. Remove %s from this object, or avoid the entire object: %s",
368368
"377": "BigInt (%s) is not yet supported in client component props. Remove %s from this object or use a plain number instead: %s",
369-
"378": "Type %s is not supported in client component props. Remove %s from this object, or avoid the entire object: %s"
369+
"378": "Type %s is not supported in client component props. Remove %s from this object, or avoid the entire object: %s",
370+
"379": "Refs cannot be used in server components, nor passed to client components."
370371
}

0 commit comments

Comments
 (0)