diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts
index b9ec688d877ed..76135f96b4249 100644
--- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts
+++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts
@@ -2327,7 +2327,7 @@ function codegenJsxAttribute(
}
}
-const JSX_TEXT_CHILD_REQUIRES_EXPR_CONTAINER_PATTERN = /[<>&]/;
+const JSX_TEXT_CHILD_REQUIRES_EXPR_CONTAINER_PATTERN = /[<>&{}]/;
function codegenJsxElement(
cx: Context,
place: Place,
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-bracket-in-text.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-bracket-in-text.expect.md
new file mode 100644
index 0000000000000..21a2f31ccdbba
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-bracket-in-text.expect.md
@@ -0,0 +1,51 @@
+
+## Input
+
+```javascript
+function Test() {
+ return (
+
+ If the string contains the string {pageNumber} it will be
+ replaced by the page number.
+
+ );
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Test,
+ params: [],
+};
+
+```
+
+## Code
+
+```javascript
+import { c as _c } from "react/compiler-runtime";
+function Test() {
+ const $ = _c(1);
+ let t0;
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
+ t0 = (
+
+ {
+ "If the string contains the string {pageNumber} it will be replaced by the page number."
+ }
+
+ );
+ $[0] = t0;
+ } else {
+ t0 = $[0];
+ }
+ return t0;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Test,
+ params: [],
+};
+
+```
+
+### Eval output
+(kind: ok) If the string contains the string {pageNumber} it will be replaced by the page number.
\ No newline at end of file
diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-bracket-in-text.jsx b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-bracket-in-text.jsx
new file mode 100644
index 0000000000000..1e93f5e0ad8f9
--- /dev/null
+++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-bracket-in-text.jsx
@@ -0,0 +1,13 @@
+function Test() {
+ return (
+
+ If the string contains the string {pageNumber} it will be
+ replaced by the page number.
+
+ );
+}
+
+export const FIXTURE_ENTRYPOINT = {
+ fn: Test,
+ params: [],
+};
diff --git a/compiler/packages/eslint-plugin-react-compiler/README.md b/compiler/packages/eslint-plugin-react-compiler/README.md
index cc70679383690..3bf175f85e873 100644
--- a/compiler/packages/eslint-plugin-react-compiler/README.md
+++ b/compiler/packages/eslint-plugin-react-compiler/README.md
@@ -29,7 +29,7 @@ import react from "eslint-plugin-react"
export default [
// Your existing config
{ ...pluginReact.configs.flat.recommended, settings: { react: { version: "detect" } } },
-+ reactCompiler.config.recommended
++ reactCompiler.configs.recommended
]
```
diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js
index 4ea66dcddd742..3d125cc3104ea 100644
--- a/packages/react-client/src/ReactFlightClient.js
+++ b/packages/react-client/src/ReactFlightClient.js
@@ -16,6 +16,7 @@ import type {
ReactTimeInfo,
ReactStackTrace,
ReactCallSite,
+ ReactErrorInfoDev,
} from 'shared/ReactTypes';
import type {LazyComponent} from 'react/src/ReactLazy';
@@ -2123,18 +2124,12 @@ function resolveErrorProd(response: Response): Error {
function resolveErrorDev(
response: Response,
- errorInfo: {
- name: string,
- message: string,
- stack: ReactStackTrace,
- env: string,
- ...
- },
+ errorInfo: ReactErrorInfoDev,
): Error {
- const name: string = errorInfo.name;
- const message: string = errorInfo.message;
- const stack: ReactStackTrace = errorInfo.stack;
- const env: string = errorInfo.env;
+ const name = errorInfo.name;
+ const message = errorInfo.message;
+ const stack = errorInfo.stack;
+ const env = errorInfo.env;
if (!__DEV__) {
// These errors should never make it into a build so we don't need to encode them in codes.json
diff --git a/packages/react-client/src/__tests__/ReactFlight-test.js b/packages/react-client/src/__tests__/ReactFlight-test.js
index 44b0c76c3f4fb..25bbd0e83acba 100644
--- a/packages/react-client/src/__tests__/ReactFlight-test.js
+++ b/packages/react-client/src/__tests__/ReactFlight-test.js
@@ -1387,6 +1387,7 @@ describe('ReactFlight', () => {
errors: [
{
message: 'This is an error',
+ name: 'Error',
stack: expect.stringContaining(
'Error: This is an error\n' +
' at eval (eval at testFunction (inspected-page.html:29:11),%20%3Canonymous%3E:1:35)\n' +
diff --git a/packages/react-debug-tools/src/ReactDebugHooks.js b/packages/react-debug-tools/src/ReactDebugHooks.js
index 2e1fcb19a6782..f30489b7f655f 100644
--- a/packages/react-debug-tools/src/ReactDebugHooks.js
+++ b/packages/react-debug-tools/src/ReactDebugHooks.js
@@ -127,6 +127,13 @@ function getPrimitiveStackCache(): Map> {
}
Dispatcher.useId();
+
+ if (typeof Dispatcher.useResourceEffect === 'function') {
+ Dispatcher.useResourceEffect(() => ({}), []);
+ }
+ if (typeof Dispatcher.useEffectEvent === 'function') {
+ Dispatcher.useEffectEvent((args: empty) => {});
+ }
} finally {
readHookLog = hookLog;
hookLog = [];
@@ -749,6 +756,20 @@ function useResourceEffect(
});
}
+function useEffectEvent) => mixed>(callback: F): F {
+ nextHook();
+ hookLog.push({
+ displayName: null,
+ primitive: 'EffectEvent',
+ stackError: new Error(),
+ value: callback,
+ debugInfo: null,
+ dispatcherHookName: 'EffectEvent',
+ });
+
+ return callback;
+}
+
const Dispatcher: DispatcherType = {
use,
readContext,
@@ -773,6 +794,7 @@ const Dispatcher: DispatcherType = {
useFormState,
useActionState,
useHostTransitionStatus,
+ useEffectEvent,
useResourceEffect,
};
@@ -962,7 +984,7 @@ function parseHookName(functionName: void | string): string {
startIndex += 'unstable_'.length;
}
- if (functionName.slice(startIndex).startsWith('unstable_')) {
+ if (functionName.slice(startIndex).startsWith('experimental_')) {
startIndex += 'experimental_'.length;
}
diff --git a/packages/react-devtools-shell/src/app/InspectableElements/InspectableElements.js b/packages/react-devtools-shell/src/app/InspectableElements/InspectableElements.js
index abc6eee336c19..95f104925b93d 100644
--- a/packages/react-devtools-shell/src/app/InspectableElements/InspectableElements.js
+++ b/packages/react-devtools-shell/src/app/InspectableElements/InspectableElements.js
@@ -19,6 +19,7 @@ import NestedProps from './NestedProps';
import SimpleValues from './SimpleValues';
import SymbolKeys from './SymbolKeys';
import UseMemoCache from './UseMemoCache';
+import UseEffectEvent from './UseEffectEvent';
// TODO Add Immutable JS example
@@ -36,6 +37,7 @@ export default function InspectableElements(): React.Node {
+
);
}
diff --git a/packages/react-devtools-shell/src/app/InspectableElements/UseEffectEvent.js b/packages/react-devtools-shell/src/app/InspectableElements/UseEffectEvent.js
new file mode 100644
index 0000000000000..e55f6a3c55e4f
--- /dev/null
+++ b/packages/react-devtools-shell/src/app/InspectableElements/UseEffectEvent.js
@@ -0,0 +1,32 @@
+import * as React from 'react';
+
+const {experimental_useEffectEvent, useState, useEffect} = React;
+
+export default function UseEffectEvent(): React.Node {
+ return (
+ <>
+
+
+ >
+ );
+}
+
+function SingleHookCase() {
+ const onClick = experimental_useEffectEvent(() => {});
+
+ return ;
+}
+
+function useCustomHook() {
+ const [state, setState] = useState();
+ const onClick = experimental_useEffectEvent(() => {});
+ useEffect(() => {});
+
+ return [state, setState, onClick];
+}
+
+function HookTreeCase() {
+ const onClick = useCustomHook();
+
+ return ;
+}
diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js
index cda88fc5c1718..0c7d12219c32c 100644
--- a/packages/react-server/src/ReactFlightServer.js
+++ b/packages/react-server/src/ReactFlightServer.js
@@ -64,6 +64,8 @@ import type {
ReactTimeInfo,
ReactStackTrace,
ReactCallSite,
+ ReactErrorInfo,
+ ReactErrorInfoDev,
} from 'shared/ReactTypes';
import type {ReactElement} from 'shared/ReactElementType';
import type {LazyComponent} from 'react/src/ReactLazy';
@@ -3093,8 +3095,8 @@ function emitPostponeChunk(
function serializeErrorValue(request: Request, error: Error): string {
if (__DEV__) {
- let name;
- let message;
+ let name: string = 'Error';
+ let message: string;
let stack: ReactStackTrace;
let env = (0, request.environmentName)();
try {
@@ -3112,7 +3114,7 @@ function serializeErrorValue(request: Request, error: Error): string {
message = 'An error occurred but serializing the error message failed.';
stack = [];
}
- const errorInfo = {name, message, stack, env};
+ const errorInfo: ReactErrorInfoDev = {name, message, stack, env};
const id = outlineModel(request, errorInfo);
return '$Z' + id.toString(16);
} else {
@@ -3129,13 +3131,15 @@ function emitErrorChunk(
digest: string,
error: mixed,
): void {
- let errorInfo: any;
+ let errorInfo: ReactErrorInfo;
if (__DEV__) {
- let message;
+ let name: string = 'Error';
+ let message: string;
let stack: ReactStackTrace;
let env = (0, request.environmentName)();
try {
if (error instanceof Error) {
+ name = error.name;
// eslint-disable-next-line react-internal/safe-string-coercion
message = String(error.message);
stack = filterStackTrace(request, error, 0);
@@ -3157,7 +3161,7 @@ function emitErrorChunk(
message = 'An error occurred but serializing the error message failed.';
stack = [];
}
- errorInfo = {digest, message, stack, env};
+ errorInfo = {digest, name, message, stack, env};
} else {
errorInfo = {digest};
}
diff --git a/packages/shared/ReactTypes.js b/packages/shared/ReactTypes.js
index 26cd57fc96f01..735f96a36f509 100644
--- a/packages/shared/ReactTypes.js
+++ b/packages/shared/ReactTypes.js
@@ -203,6 +203,20 @@ export type ReactEnvironmentInfo = {
+env: string,
};
+export type ReactErrorInfoProd = {
+ +digest: string,
+};
+
+export type ReactErrorInfoDev = {
+ +digest?: string,
+ +name: string,
+ +message: string,
+ +stack: ReactStackTrace,
+ +env: string,
+};
+
+export type ReactErrorInfo = ReactErrorInfoProd | ReactErrorInfoDev;
+
export type ReactAsyncInfo = {
+type: string,
// Stashed Data for the Specific Execution Environment. Not part of the transport protocol