diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js index 243c7dba8607c..4d821091a2750 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js @@ -5292,6 +5292,51 @@ 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']); + + 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..e195bd62a0200 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 { @@ -772,7 +775,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..c0f0660efe212 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 { @@ -772,7 +775,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..2a3d5cefa1bca 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,37 @@ 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 () => { + 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..a5a485db70189 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,13 @@ function use(usable: Usable): T { } } } - } else { - // TODO: Add support for Context + } else if ( + usable.$$typeof != null && + (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..79dfee587116c 100644 --- a/packages/react-server/src/ReactFlightHooks.js +++ b/packages/react-server/src/ReactFlightHooks.js @@ -200,8 +200,12 @@ function use(usable: Usable): T { } } } - } else { - // TODO: Add support for Context + } else if ( + usable.$$typeof != null && + 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;