@@ -69,7 +69,7 @@ function _assertThisInitialized(self) {
69
69
return self;
70
70
}
71
71
72
- var ReactVersion = "18.3.0-www-classic-9da53968 ";
72
+ var ReactVersion = "18.3.0-www-classic-4bc5632c ";
73
73
74
74
var LegacyRoot = 0;
75
75
var ConcurrentRoot = 1;
@@ -3013,7 +3013,14 @@ function describeNativeComponentFrame(fn, construct) {
3013
3013
// tries to access React/ReactDOM/props. We should probably make this throw
3014
3014
// in simple components too
3015
3015
3016
- fn();
3016
+ var maybePromise = fn(); // If the function component returns a promise, it's likely an async
3017
+ // component, which we don't yet support. Attach a noop catch handler to
3018
+ // silence the error.
3019
+ // TODO: Implement component stacks for async client components?
3020
+
3021
+ if (maybePromise && typeof maybePromise.catch === "function") {
3022
+ maybePromise.catch(function () {});
3023
+ }
3017
3024
}
3018
3025
} catch (sample) {
3019
3026
// This is inlined manually because closure doesn't do it for us.
@@ -5401,6 +5408,7 @@ function trackUsedThenable(thenableState, thenable, index) {
5401
5408
5402
5409
case "rejected": {
5403
5410
var rejectedError = thenable.reason;
5411
+ checkIfUseWrappedInAsyncCatch(rejectedError);
5404
5412
throw rejectedError;
5405
5413
}
5406
5414
@@ -5456,18 +5464,20 @@ function trackUsedThenable(thenableState, thenable, index) {
5456
5464
rejectedThenable.reason = error;
5457
5465
}
5458
5466
}
5459
- );
5460
- } // Check one more time in case the thenable resolved synchronously.
5467
+ ); // Check one more time in case the thenable resolved synchronously.
5461
5468
5462
- switch (thenable.status) {
5463
- case "fulfilled": {
5464
- var fulfilledThenable = thenable;
5465
- return fulfilledThenable.value;
5466
- }
5469
+ switch (thenable.status) {
5470
+ case "fulfilled": {
5471
+ var fulfilledThenable = thenable;
5472
+ return fulfilledThenable.value;
5473
+ }
5467
5474
5468
- case "rejected": {
5469
- var rejectedThenable = thenable;
5470
- throw rejectedThenable.reason;
5475
+ case "rejected": {
5476
+ var rejectedThenable = thenable;
5477
+ var _rejectedError = rejectedThenable.reason;
5478
+ checkIfUseWrappedInAsyncCatch(_rejectedError);
5479
+ throw _rejectedError;
5480
+ }
5471
5481
}
5472
5482
} // Suspend.
5473
5483
//
@@ -5526,6 +5536,22 @@ function checkIfUseWrappedInTryCatch() {
5526
5536
5527
5537
return false;
5528
5538
}
5539
+ function checkIfUseWrappedInAsyncCatch(rejectedReason) {
5540
+ // This check runs in prod, too, because it prevents a more confusing
5541
+ // downstream error, where SuspenseException is caught by a promise and
5542
+ // thrown asynchronously.
5543
+ // TODO: Another way to prevent SuspenseException from leaking into an async
5544
+ // execution context is to check the dispatcher every time `use` is called,
5545
+ // or some equivalent. That might be preferable for other reasons, too, since
5546
+ // it matches how we prevent similar mistakes for other hooks.
5547
+ if (rejectedReason === SuspenseException) {
5548
+ throw new Error(
5549
+ "Hooks are not supported inside an async component. This " +
5550
+ "error is often caused by accidentally adding `'use client'` " +
5551
+ "to a module that was originally written for the server."
5552
+ );
5553
+ }
5554
+ }
5529
5555
5530
5556
var thenableState$1 = null;
5531
5557
var thenableIndexCounter$1 = 0;
@@ -7843,10 +7869,12 @@ var ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher,
7843
7869
var didWarnAboutMismatchedHooksForComponent;
7844
7870
var didWarnUncachedGetSnapshot;
7845
7871
var didWarnAboutUseWrappedInTryCatch;
7872
+ var didWarnAboutAsyncClientComponent;
7846
7873
7847
7874
{
7848
7875
didWarnAboutMismatchedHooksForComponent = new Set();
7849
7876
didWarnAboutUseWrappedInTryCatch = new Set();
7877
+ didWarnAboutAsyncClientComponent = new Set();
7850
7878
} // The effect "instance" is a shared object that remains the same for the entire
7851
7879
// lifetime of an effect. In Rust terms, a RefCell. We use it to store the
7852
7880
// "destroy" function that is returned from an effect, because that is stateful.
@@ -7987,6 +8015,57 @@ function warnOnHookMismatchInDev(currentHookName) {
7987
8015
}
7988
8016
}
7989
8017
8018
+ function warnIfAsyncClientComponent(Component, componentDoesIncludeHooks) {
8019
+ {
8020
+ // This dev-only check only works for detecting native async functions,
8021
+ // not transpiled ones. There's also a prod check that we use to prevent
8022
+ // async client components from crashing the app; the prod one works even
8023
+ // for transpiled async functions. Neither mechanism is completely
8024
+ // bulletproof but together they cover the most common cases.
8025
+ var isAsyncFunction = // $FlowIgnore[method-unbinding]
8026
+ Object.prototype.toString.call(Component) === "[object AsyncFunction]";
8027
+
8028
+ if (isAsyncFunction) {
8029
+ // Encountered an async Client Component. This is not yet supported,
8030
+ // except in certain constrained cases, like during a route navigation.
8031
+ var componentName = getComponentNameFromFiber(currentlyRenderingFiber$1);
8032
+
8033
+ if (!didWarnAboutAsyncClientComponent.has(componentName)) {
8034
+ didWarnAboutAsyncClientComponent.add(componentName); // Check if this is a sync update. We use the "root" render lanes here
8035
+ // because the "subtree" render lanes may include additional entangled
8036
+ // lanes related to revealing previously hidden content.
8037
+
8038
+ var root = getWorkInProgressRoot();
8039
+ var rootRenderLanes = getWorkInProgressRootRenderLanes();
8040
+
8041
+ if (root !== null && includesBlockingLane(root, rootRenderLanes)) {
8042
+ error(
8043
+ "async/await is not yet supported in Client Components, only " +
8044
+ "Server Components. This error is often caused by accidentally " +
8045
+ "adding `'use client'` to a module that was originally written " +
8046
+ "for the server."
8047
+ );
8048
+ } else {
8049
+ // This is a concurrent (Transition, Retry, etc) render. We don't
8050
+ // warn in these cases.
8051
+ //
8052
+ // However, Async Components are forbidden to include hooks, even
8053
+ // during a transition, so let's check for that here.
8054
+ //
8055
+ // TODO: Add a corresponding warning to Server Components runtime.
8056
+ if (componentDoesIncludeHooks) {
8057
+ error(
8058
+ "Hooks are not supported inside an async component. This " +
8059
+ "error is often caused by accidentally adding `'use client'` " +
8060
+ "to a module that was originally written for the server."
8061
+ );
8062
+ }
8063
+ }
8064
+ }
8065
+ }
8066
+ }
8067
+ }
8068
+
7990
8069
function throwInvalidHookError() {
7991
8070
throw new Error(
7992
8071
"Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for" +
@@ -8156,18 +8235,20 @@ function renderWithHooks(
8156
8235
}
8157
8236
}
8158
8237
8159
- finishRenderingHooks(current, workInProgress);
8238
+ finishRenderingHooks(current, workInProgress, Component );
8160
8239
return children;
8161
8240
}
8162
8241
8163
- function finishRenderingHooks(current, workInProgress) {
8164
- // We can assume the previous dispatcher is always this one, since we set it
8165
- // at the beginning of the render phase and there's no re-entrance.
8166
- ReactCurrentDispatcher$1.current = ContextOnlyDispatcher;
8167
-
8242
+ function finishRenderingHooks(current, workInProgress, Component) {
8168
8243
{
8169
8244
workInProgress._debugHookTypes = hookTypesDev;
8170
- } // This check uses currentHook so that it works the same in DEV and prod bundles.
8245
+ var componentDoesIncludeHooks =
8246
+ workInProgressHook !== null || thenableIndexCounter !== 0;
8247
+ warnIfAsyncClientComponent(Component, componentDoesIncludeHooks);
8248
+ } // We can assume the previous dispatcher is always this one, since we set it
8249
+ // at the beginning of the render phase and there's no re-entrance.
8250
+
8251
+ ReactCurrentDispatcher$1.current = ContextOnlyDispatcher; // This check uses currentHook so that it works the same in DEV and prod bundles.
8171
8252
// hookTypesDev could catch more cases (e.g. context) but only in DEV bundles.
8172
8253
8173
8254
var didRenderTooFewHooks = currentHook !== null && currentHook.next !== null;
@@ -8240,7 +8321,12 @@ function finishRenderingHooks(current, workInProgress) {
8240
8321
var componentName =
8241
8322
getComponentNameFromFiber(workInProgress) || "Unknown";
8242
8323
8243
- if (!didWarnAboutUseWrappedInTryCatch.has(componentName)) {
8324
+ if (
8325
+ !didWarnAboutUseWrappedInTryCatch.has(componentName) && // This warning also fires if you suspend with `use` inside an
8326
+ // async component. Since we warn for that above, we'll silence this
8327
+ // second warning by checking here.
8328
+ !didWarnAboutAsyncClientComponent.has(componentName)
8329
+ ) {
8244
8330
didWarnAboutUseWrappedInTryCatch.add(componentName);
8245
8331
8246
8332
error(
@@ -8280,7 +8366,7 @@ function replaySuspendedComponentWithHooks(
8280
8366
props,
8281
8367
secondArg
8282
8368
);
8283
- finishRenderingHooks(current, workInProgress);
8369
+ finishRenderingHooks(current, workInProgress, Component );
8284
8370
return children;
8285
8371
}
8286
8372
0 commit comments