diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
index 243c7dba8607c..4d8e677e03739 100644
--- a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
@@ -5292,6 +5292,54 @@ describe('ReactDOMFizzServer', () => {
expect(getVisibleChildren(container)).toEqual('ABC');
});
+ // @gate enableUseHook
+ it('basic use(context)', async () => {
+ const ContextA = React.createContext('default');
+ const ContextB = React.createContext('B');
+ const ServerContext = React.createServerContext(
+ 'ServerContext',
+ 'default',
+ );
+ function Client() {
+ return use(ContextA) + use(ContextB);
+ }
+ function ServerComponent() {
+ return use(ServerContext);
+ }
+ function Server() {
+ return (
+
+
+
+ );
+ }
+ function App() {
+ return (
+ <>
+
+
+
+
+ >
+ );
+ }
+
+ await act(async () => {
+ const {pipe} = ReactDOMFizzServer.renderToPipeableStream();
+ pipe(writable);
+ });
+ expect(getVisibleChildren(container)).toEqual(['AB', 'C']);
+
+ // Hydration uses a different renderer runtime (Fiber instead of Fizz).
+ // We reset _currentRenderer here to not trigger a warning about multiple
+ // renderers concurrently using these contexts
+ ContextA._currentRenderer = null;
+ ServerContext._currentRenderer = null;
+ ReactDOMClient.hydrateRoot(container, );
+ expect(Scheduler).toFlushAndYield([]);
+ expect(getVisibleChildren(container)).toEqual(['AB', 'C']);
+ });
+
// @gate enableUseHook
it('use(promise) in multiple components', async () => {
const promiseA = Promise.resolve('A');
diff --git a/packages/react-reconciler/src/ReactFiberHooks.new.js b/packages/react-reconciler/src/ReactFiberHooks.new.js
index ee3d118a4a5a7..6ca089ff2d0a8 100644
--- a/packages/react-reconciler/src/ReactFiberHooks.new.js
+++ b/packages/react-reconciler/src/ReactFiberHooks.new.js
@@ -36,7 +36,10 @@ import {
enableUseHook,
enableUseMemoCacheHook,
} from 'shared/ReactFeatureFlags';
-import {REACT_CONTEXT_TYPE} from 'shared/ReactSymbols';
+import {
+ REACT_CONTEXT_TYPE,
+ REACT_SERVER_CONTEXT_TYPE,
+} from 'shared/ReactSymbols';
import {NoMode, ConcurrentMode, DebugTracingMode} from './ReactTypeOfMode';
import {
@@ -771,8 +774,8 @@ function use(usable: Usable): T {
}
}
} else if (
- usable.$$typeof != null &&
- usable.$$typeof === REACT_CONTEXT_TYPE
+ usable.$$typeof === REACT_CONTEXT_TYPE ||
+ usable.$$typeof === REACT_SERVER_CONTEXT_TYPE
) {
const context: ReactContext = (usable: any);
return readContext(context);
diff --git a/packages/react-reconciler/src/ReactFiberHooks.old.js b/packages/react-reconciler/src/ReactFiberHooks.old.js
index aaa5ed306e42c..133dc8d5dd6eb 100644
--- a/packages/react-reconciler/src/ReactFiberHooks.old.js
+++ b/packages/react-reconciler/src/ReactFiberHooks.old.js
@@ -36,7 +36,10 @@ import {
enableUseHook,
enableUseMemoCacheHook,
} from 'shared/ReactFeatureFlags';
-import {REACT_CONTEXT_TYPE} from 'shared/ReactSymbols';
+import {
+ REACT_CONTEXT_TYPE,
+ REACT_SERVER_CONTEXT_TYPE,
+} from 'shared/ReactSymbols';
import {NoMode, ConcurrentMode, DebugTracingMode} from './ReactTypeOfMode';
import {
@@ -771,8 +774,8 @@ function use(usable: Usable): T {
}
}
} else if (
- usable.$$typeof != null &&
- usable.$$typeof === REACT_CONTEXT_TYPE
+ usable.$$typeof === REACT_CONTEXT_TYPE ||
+ usable.$$typeof === REACT_SERVER_CONTEXT_TYPE
) {
const context: ReactContext = (usable: any);
return readContext(context);
diff --git a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js
index 1b59c72ab3e48..f477bba451a6e 100644
--- a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js
+++ b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js
@@ -594,6 +594,40 @@ describe('ReactFlightDOMBrowser', () => {
expect(container.innerHTML).toBe('ABC');
});
+ // @gate enableUseHook
+ it('basic use(context)', async () => {
+ const ContextA = React.createServerContext('ContextA', '');
+ const ContextB = React.createServerContext('ContextB', 'B');
+
+ function ServerComponent() {
+ return use(ContextA) + use(ContextB);
+ }
+ function Server() {
+ return (
+
+
+
+ );
+ }
+ const stream = ReactServerDOMWriter.renderToReadableStream();
+ const response = ReactServerDOMReader.createFromReadableStream(stream);
+
+ function Client() {
+ return response.readRoot();
+ }
+
+ const container = document.createElement('div');
+ const root = ReactDOMClient.createRoot(container);
+ await act(async () => {
+ // Client uses a different renderer.
+ // We reset _currentRenderer here to not trigger a warning about multiple
+ // renderers concurrently using this context
+ ContextA._currentRenderer = null;
+ root.render();
+ });
+ expect(container.innerHTML).toBe('AB');
+ });
+
// @gate enableUseHook
it('use(promise) in multiple components', async () => {
function Child({prefix}) {
diff --git a/packages/react-server/src/ReactFizzHooks.js b/packages/react-server/src/ReactFizzHooks.js
index 02072fabd3f37..b330605260afd 100644
--- a/packages/react-server/src/ReactFizzHooks.js
+++ b/packages/react-server/src/ReactFizzHooks.js
@@ -39,6 +39,10 @@ import {
enableUseMemoCacheHook,
} from 'shared/ReactFeatureFlags';
import is from 'shared/objectIs';
+import {
+ REACT_SERVER_CONTEXT_TYPE,
+ REACT_CONTEXT_TYPE,
+} from 'shared/ReactSymbols';
type BasicStateAction = (S => S) | S;
type Dispatch = A => void;
@@ -616,8 +620,12 @@ function use(usable: Usable): T {
}
}
}
- } else {
- // TODO: Add support for Context
+ } else if (
+ usable.$$typeof === REACT_CONTEXT_TYPE ||
+ usable.$$typeof === REACT_SERVER_CONTEXT_TYPE
+ ) {
+ const context: ReactContext = (usable: any);
+ return readContext(context);
}
}
diff --git a/packages/react-server/src/ReactFlightHooks.js b/packages/react-server/src/ReactFlightHooks.js
index f4784c82e1cf1..dfb234b15ad27 100644
--- a/packages/react-server/src/ReactFlightHooks.js
+++ b/packages/react-server/src/ReactFlightHooks.js
@@ -200,8 +200,9 @@ function use(usable: Usable): T {
}
}
}
- } else {
- // TODO: Add support for Context
+ } else if (usable.$$typeof === REACT_SERVER_CONTEXT_TYPE) {
+ const context: ReactServerContext = (usable: any);
+ return readContext(context);
}
}
diff --git a/packages/shared/ReactTypes.js b/packages/shared/ReactTypes.js
index 9ea076b15f9e7..2832535c87c95 100644
--- a/packages/shared/ReactTypes.js
+++ b/packages/shared/ReactTypes.js
@@ -213,5 +213,4 @@ export type StartTransitionOptions = {|
name?: string,
|};
-// TODO: Add Context support
export type Usable = Thenable | ReactContext;