diff --git a/packages/react-art/src/ReactARTHostConfig.js b/packages/react-art/src/ReactARTHostConfig.js index ec702bf9a70d4..47bced8a1274a 100644 --- a/packages/react-art/src/ReactARTHostConfig.js +++ b/packages/react-art/src/ReactARTHostConfig.js @@ -436,24 +436,6 @@ export function getInstanceFromNode(node) { throw new Error('Not implemented.'); } -export function isOpaqueHydratingObject(value: mixed): boolean { - throw new Error('Not implemented.'); -} - -export function makeOpaqueHydratingObject( - attemptToReadValue: () => void, -): OpaqueIDType { - throw new Error('Not implemented.'); -} - -export function makeClientId(): OpaqueIDType { - throw new Error('Not implemented.'); -} - -export function makeClientIdInDEV(warnOnAccessInDEV: () => void): OpaqueIDType { - throw new Error('Not implemented.'); -} - export function beforeActiveInstanceBlur(internalInstanceHandle: Object) { // noop } diff --git a/packages/react-debug-tools/src/ReactDebugHooks.js b/packages/react-debug-tools/src/ReactDebugHooks.js index eed8c46df7d6d..895e75359867c 100644 --- a/packages/react-debug-tools/src/ReactDebugHooks.js +++ b/packages/react-debug-tools/src/ReactDebugHooks.js @@ -18,13 +18,9 @@ import type { Fiber, Dispatcher as DispatcherType, } from 'react-reconciler/src/ReactInternalTypes'; -import type {OpaqueIDType} from 'react-reconciler/src/ReactFiberHostConfig'; - -import {NoMode} from 'react-reconciler/src/ReactTypeOfMode'; import ErrorStackParser from 'error-stack-parser'; import ReactSharedInternals from 'shared/ReactSharedInternals'; -import {REACT_OPAQUE_ID_TYPE} from 'shared/ReactSymbols'; import { FunctionComponent, SimpleMemoComponent, @@ -53,8 +49,6 @@ type Dispatch = A => void; let primitiveStackCache: null | Map> = null; -let currentFiber: Fiber | null = null; - type Hook = { memoizedState: any, next: Hook | null, @@ -324,23 +318,6 @@ function useDeferredValue(value: T): T { return value; } -function useOpaqueIdentifier(): OpaqueIDType | void { - const hook = nextHook(); // State - if (currentFiber && currentFiber.mode === NoMode) { - nextHook(); // Effect - } - let value = hook === null ? undefined : hook.memoizedState; - if (value && value.$$typeof === REACT_OPAQUE_ID_TYPE) { - value = undefined; - } - hookLog.push({ - primitive: 'OpaqueIdentifier', - stackError: new Error(), - value, - }); - return value; -} - function useId(): string { const hook = nextHook(); const id = hook !== null ? hook.memoizedState : ''; @@ -371,7 +348,6 @@ const Dispatcher: DispatcherType = { useMutableSource, useSyncExternalStore, useDeferredValue, - useOpaqueIdentifier, useId, }; @@ -767,8 +743,6 @@ export function inspectHooksOfFiber( currentDispatcher = ReactSharedInternals.ReactCurrentDispatcher; } - currentFiber = fiber; - if ( fiber.tag !== FunctionComponent && fiber.tag !== SimpleMemoComponent && diff --git a/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js b/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js index d17a01a258277..559a0bf75e52f 100644 --- a/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js +++ b/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js @@ -598,64 +598,6 @@ describe('ReactHooksInspectionIntegration', () => { ]); }); - it('should support composite useOpaqueIdentifier hook', () => { - function Foo(props) { - const id = React.unstable_useOpaqueIdentifier(); - const [state] = React.useState(() => 'hello', []); - return
{state}
; - } - - const renderer = ReactTestRenderer.create(); - const childFiber = renderer.root.findByType(Foo)._currentFiber(); - const tree = ReactDebugTools.inspectHooksOfFiber(childFiber); - - expect(tree.length).toEqual(2); - - expect(tree[0].id).toEqual(0); - expect(tree[0].isStateEditable).toEqual(false); - expect(tree[0].name).toEqual('OpaqueIdentifier'); - expect(String(tree[0].value).startsWith('c_')).toBe(true); - - expect(tree[1]).toEqual({ - id: 1, - isStateEditable: true, - name: 'State', - value: 'hello', - subHooks: [], - }); - }); - - it('should support composite useOpaqueIdentifier hook in concurrent mode', () => { - function Foo(props) { - const id = React.unstable_useOpaqueIdentifier(); - const [state] = React.useState('hello'); - return
{state}
; - } - - const renderer = ReactTestRenderer.create(, { - unstable_isConcurrent: true, - }); - expect(Scheduler).toFlushWithoutYielding(); - - const childFiber = renderer.root.findByType(Foo)._currentFiber(); - const tree = ReactDebugTools.inspectHooksOfFiber(childFiber); - - expect(tree.length).toEqual(2); - - expect(tree[0].id).toEqual(0); - expect(tree[0].isStateEditable).toEqual(false); - expect(tree[0].name).toEqual('OpaqueIdentifier'); - expect(String(tree[0].value).startsWith('c_')).toBe(true); - - expect(tree[1]).toEqual({ - id: 1, - isStateEditable: true, - name: 'State', - value: 'hello', - subHooks: [], - }); - }); - it('should support useId hook', () => { function Foo(props) { const id = React.unstable_useId(); diff --git a/packages/react-devtools-shared/src/backend/ReactSymbols.js b/packages/react-devtools-shared/src/backend/ReactSymbols.js index c6515f02edc51..ebc6920be8d2f 100644 --- a/packages/react-devtools-shared/src/backend/ReactSymbols.js +++ b/packages/react-devtools-shared/src/backend/ReactSymbols.js @@ -40,9 +40,6 @@ export const LAZY_SYMBOL_STRING = 'Symbol(react.lazy)'; export const MEMO_NUMBER = 0xead3; export const MEMO_SYMBOL_STRING = 'Symbol(react.memo)'; -export const OPAQUE_ID_NUMBER = 0xeae0; -export const OPAQUE_ID_SYMBOL_STRING = 'Symbol(react.opaque.id)'; - export const PORTAL_NUMBER = 0xeaca; export const PORTAL_SYMBOL_STRING = 'Symbol(react.portal)'; diff --git a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.js b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.js index ba2b22437bc1b..114c522313a79 100644 --- a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.js @@ -17,8 +17,6 @@ let React; let ReactDOM; let ReactDOMServer; let ReactTestUtils; -let act; -let Scheduler; let useState; let useReducer; let useEffect; @@ -30,7 +28,6 @@ let useImperativeHandle; let useInsertionEffect; let useLayoutEffect; let useDebugValue; -let useOpaqueIdentifier; let forwardRef; let yieldedValues; let yieldValue; @@ -44,8 +41,6 @@ function initModules() { ReactDOM = require('react-dom'); ReactDOMServer = require('react-dom/server'); ReactTestUtils = require('react-dom/test-utils'); - Scheduler = require('scheduler'); - act = require('jest-react').act; useState = React.useState; useReducer = React.useReducer; useEffect = React.useEffect; @@ -57,7 +52,6 @@ function initModules() { useImperativeHandle = React.useImperativeHandle; useInsertionEffect = React.useInsertionEffect; useLayoutEffect = React.useLayoutEffect; - useOpaqueIdentifier = React.unstable_useOpaqueIdentifier; forwardRef = React.forwardRef; yieldedValues = []; @@ -83,9 +77,6 @@ const { itRenders, itThrowsWhenRendering, serverRender, - streamRender, - clientCleanRender, - clientRenderOnServerString, } = ReactDOMServerIntegrationUtils(initModules); describe('ReactDOMServerHooks', () => { @@ -910,1104 +901,4 @@ describe('ReactDOMServerHooks', () => { ); expect(container.children[0].textContent).toEqual('0'); }); - - describe('useOpaqueIdentifier', () => { - it('generates unique ids for server string render', async () => { - function App(props) { - const idOne = useOpaqueIdentifier(); - const idTwo = useOpaqueIdentifier(); - return ( -
-
-
- - -
- ); - } - - const domNode = await serverRender(); - expect(domNode.children.length).toEqual(4); - expect(domNode.children[0].getAttribute('aria-labelledby')).toEqual( - domNode.children[1].getAttribute('id'), - ); - expect(domNode.children[2].getAttribute('aria-labelledby')).toEqual( - domNode.children[3].getAttribute('id'), - ); - expect(domNode.children[0].getAttribute('aria-labelledby')).not.toEqual( - domNode.children[2].getAttribute('aria-labelledby'), - ); - expect( - domNode.children[0].getAttribute('aria-labelledby'), - ).not.toBeNull(); - expect( - domNode.children[2].getAttribute('aria-labelledby'), - ).not.toBeNull(); - }); - - it('generates unique ids for server stream render', async () => { - function App(props) { - const idOne = useOpaqueIdentifier(); - const idTwo = useOpaqueIdentifier(); - return ( -
-
-
- - -
- ); - } - - const domNode = await streamRender(); - expect(domNode.children.length).toEqual(4); - expect(domNode.children[0].getAttribute('aria-labelledby')).toEqual( - domNode.children[1].getAttribute('id'), - ); - expect(domNode.children[2].getAttribute('aria-labelledby')).toEqual( - domNode.children[3].getAttribute('id'), - ); - expect(domNode.children[0].getAttribute('aria-labelledby')).not.toEqual( - domNode.children[2].getAttribute('aria-labelledby'), - ); - expect( - domNode.children[0].getAttribute('aria-labelledby'), - ).not.toBeNull(); - expect( - domNode.children[2].getAttribute('aria-labelledby'), - ).not.toBeNull(); - }); - - it('generates unique ids for client render', async () => { - function App(props) { - const idOne = useOpaqueIdentifier(); - const idTwo = useOpaqueIdentifier(); - return ( -
-
-
- - -
- ); - } - - const domNode = await clientCleanRender(); - expect(domNode.children.length).toEqual(4); - expect(domNode.children[0].getAttribute('aria-labelledby')).toEqual( - domNode.children[1].getAttribute('id'), - ); - expect(domNode.children[2].getAttribute('aria-labelledby')).toEqual( - domNode.children[3].getAttribute('id'), - ); - expect(domNode.children[0].getAttribute('aria-labelledby')).not.toEqual( - domNode.children[2].getAttribute('aria-labelledby'), - ); - expect( - domNode.children[0].getAttribute('aria-labelledby'), - ).not.toBeNull(); - expect( - domNode.children[2].getAttribute('aria-labelledby'), - ).not.toBeNull(); - }); - - it('generates unique ids for client render on good server markup', async () => { - function App(props) { - const idOne = useOpaqueIdentifier(); - const idTwo = useOpaqueIdentifier(); - return ( -
-
-
- - -
- ); - } - - const domNode = await clientRenderOnServerString(); - expect(domNode.children.length).toEqual(4); - expect(domNode.children[0].getAttribute('aria-labelledby')).toEqual( - domNode.children[1].getAttribute('id'), - ); - expect(domNode.children[2].getAttribute('aria-labelledby')).toEqual( - domNode.children[3].getAttribute('id'), - ); - expect(domNode.children[0].getAttribute('aria-labelledby')).not.toEqual( - domNode.children[2].getAttribute('aria-labelledby'), - ); - expect( - domNode.children[0].getAttribute('aria-labelledby'), - ).not.toBeNull(); - expect( - domNode.children[2].getAttribute('aria-labelledby'), - ).not.toBeNull(); - }); - - it('useOpaqueIdentifier does not change id even if the component updates during client render', async () => { - let _setShowId; - function App() { - const id = useOpaqueIdentifier(); - const [showId, setShowId] = useState(false); - _setShowId = setShowId; - return ( -
-
- {showId &&
} -
- ); - } - - const domNode = await clientCleanRender(); - const oldClientId = domNode.children[0].getAttribute('aria-labelledby'); - - expect(domNode.children.length).toEqual(1); - expect(oldClientId).not.toBeNull(); - - await act(async () => _setShowId(true)); - - expect(domNode.children.length).toEqual(2); - expect(domNode.children[0].getAttribute('aria-labelledby')).toEqual( - domNode.children[1].getAttribute('id'), - ); - expect(domNode.children[0].getAttribute('aria-labelledby')).toEqual( - oldClientId, - ); - }); - - it('useOpaqueIdentifier identifierPrefix works for server renderer and does not clash', async () => { - function ChildTwo({id}) { - return
Child Three
; - } - function App() { - const id = useOpaqueIdentifier(); - const idTwo = useOpaqueIdentifier(); - - return ( -
-
Child One
- -
Child Three
-
Child Four
-
- ); - } - - const containerOne = document.createElement('div'); - document.body.append(containerOne); - - containerOne.innerHTML = ReactDOMServer.renderToString(, { - identifierPrefix: 'one', - }); - - const containerTwo = document.createElement('div'); - document.body.append(containerTwo); - - containerTwo.innerHTML = ReactDOMServer.renderToString(, { - identifierPrefix: 'two', - }); - - expect(document.body.children.length).toEqual(2); - const childOne = document.body.children[0]; - const childTwo = document.body.children[1]; - - expect( - childOne.children[0].children[0].getAttribute('aria-labelledby'), - ).toEqual(childOne.children[0].children[1].getAttribute('id')); - expect( - childOne.children[0].children[2].getAttribute('aria-labelledby'), - ).toEqual(childOne.children[0].children[3].getAttribute('id')); - - expect( - childOne.children[0].children[0].getAttribute('aria-labelledby'), - ).not.toEqual( - childOne.children[0].children[2].getAttribute('aria-labelledby'), - ); - - expect( - childOne.children[0].children[0] - .getAttribute('aria-labelledby') - .startsWith('one'), - ).toBe(true); - expect( - childOne.children[0].children[2] - .getAttribute('aria-labelledby') - .includes('one'), - ).toBe(true); - - expect( - childTwo.children[0].children[0].getAttribute('aria-labelledby'), - ).toEqual(childTwo.children[0].children[1].getAttribute('id')); - expect( - childTwo.children[0].children[2].getAttribute('aria-labelledby'), - ).toEqual(childTwo.children[0].children[3].getAttribute('id')); - - expect( - childTwo.children[0].children[0].getAttribute('aria-labelledby'), - ).not.toEqual( - childTwo.children[0].children[2].getAttribute('aria-labelledby'), - ); - - expect( - childTwo.children[0].children[0] - .getAttribute('aria-labelledby') - .startsWith('two'), - ).toBe(true); - expect( - childTwo.children[0].children[2] - .getAttribute('aria-labelledby') - .startsWith('two'), - ).toBe(true); - }); - - it('useOpaqueIdentifier identifierPrefix works for multiple reads on a streaming server renderer', async () => { - function ChildTwo() { - const id = useOpaqueIdentifier(); - - return
Child Two
; - } - - function App() { - const id = useOpaqueIdentifier(); - - return ( - <> -
Child One
- -
Aria One
- - ); - } - - const container = document.createElement('div'); - document.body.append(container); - - const streamOne = ReactDOMServer.renderToNodeStream(, { - identifierPrefix: 'one', - }).setEncoding('utf8'); - const streamTwo = ReactDOMServer.renderToNodeStream(, { - identifierPrefix: 'two', - }).setEncoding('utf8'); - - const streamOneIsDone = new Promise((resolve, reject) => { - streamOne.on('end', () => resolve()); - streamOne.on('error', e => reject(e)); - }); - const streamTwoIsDone = new Promise((resolve, reject) => { - streamTwo.on('end', () => resolve()); - streamTwo.on('error', e => reject(e)); - }); - - const containerOne = document.createElement('div'); - const containerTwo = document.createElement('div'); - - streamOne._read(10); - streamTwo._read(10); - - containerOne.innerHTML = streamOne.read(); - containerTwo.innerHTML = streamTwo.read(); - - expect(containerOne.children[0].getAttribute('id')).not.toEqual( - containerOne.children[1].getAttribute('id'), - ); - expect(containerTwo.children[0].getAttribute('id')).not.toEqual( - containerTwo.children[1].getAttribute('id'), - ); - expect(containerOne.children[0].getAttribute('id')).not.toEqual( - containerTwo.children[0].getAttribute('id'), - ); - expect(containerOne.children[0].getAttribute('id').includes('one')).toBe( - true, - ); - expect(containerOne.children[1].getAttribute('id').includes('one')).toBe( - true, - ); - expect(containerTwo.children[0].getAttribute('id').includes('two')).toBe( - true, - ); - expect(containerTwo.children[1].getAttribute('id').includes('two')).toBe( - true, - ); - - expect(containerOne.children[1].getAttribute('id')).not.toEqual( - containerTwo.children[1].getAttribute('id'), - ); - expect(containerOne.children[0].getAttribute('id')).toEqual( - containerOne.children[2].getAttribute('aria-labelledby'), - ); - expect(containerTwo.children[0].getAttribute('id')).toEqual( - containerTwo.children[2].getAttribute('aria-labelledby'), - ); - - // Exhaust the rest of the stream - class Sink extends require('stream').Writable { - _write(chunk, encoding, done) { - done(); - } - } - streamOne.pipe(new Sink()); - streamTwo.pipe(new Sink()); - - await Promise.all([streamOneIsDone, streamTwoIsDone]); - }); - - it('useOpaqueIdentifier: IDs match when, after hydration, a new component that uses the ID is rendered', async () => { - let _setShowDiv; - function App() { - const id = useOpaqueIdentifier(); - const [showDiv, setShowDiv] = useState(false); - _setShowDiv = setShowDiv; - - return ( -
-
Child One
- {showDiv &&
Child Two
} -
- ); - } - - const container = document.createElement('div'); - document.body.append(container); - - container.innerHTML = ReactDOMServer.renderToString(); - const root = ReactDOM.createRoot(container, {hydrate: true}); - root.render(); - Scheduler.unstable_flushAll(); - jest.runAllTimers(); - - expect(container.children[0].children.length).toEqual(1); - const oldServerId = container.children[0].children[0].getAttribute('id'); - expect(oldServerId).not.toBeNull(); - - await act(async () => { - _setShowDiv(true); - }); - expect(container.children[0].children.length).toEqual(2); - expect(container.children[0].children[0].getAttribute('id')).toEqual( - container.children[0].children[1].getAttribute('id'), - ); - expect(container.children[0].children[0].getAttribute('id')).not.toEqual( - oldServerId, - ); - expect( - container.children[0].children[0].getAttribute('id'), - ).not.toBeNull(); - }); - - it('useOpaqueIdentifier: IDs match when, after hydration, a new component that uses the ID is rendered for legacy', async () => { - let _setShowDiv; - function App() { - const id = useOpaqueIdentifier(); - const [showDiv, setShowDiv] = useState(false); - _setShowDiv = setShowDiv; - - return ( -
-
Child One
- {showDiv &&
Child Two
} -
- ); - } - - const container = document.createElement('div'); - document.body.append(container); - - container.innerHTML = ReactDOMServer.renderToString(); - ReactDOM.hydrate(, container); - - expect(container.children[0].children.length).toEqual(1); - const oldServerId = container.children[0].children[0].getAttribute('id'); - expect(oldServerId).not.toBeNull(); - - await act(async () => { - _setShowDiv(true); - }); - expect(container.children[0].children.length).toEqual(2); - expect(container.children[0].children[0].getAttribute('id')).toEqual( - container.children[0].children[1].getAttribute('id'), - ); - expect(container.children[0].children[0].getAttribute('id')).not.toEqual( - oldServerId, - ); - expect( - container.children[0].children[0].getAttribute('id'), - ).not.toBeNull(); - }); - - it('useOpaqueIdentifier: ID is not used during hydration but is used in an update', async () => { - let _setShow; - function App({unused}) { - Scheduler.unstable_yieldValue('App'); - const id = useOpaqueIdentifier(); - const [show, setShow] = useState(false); - _setShow = setShow; - return ( -
- {'Child One'} -
- ); - } - - const container = document.createElement('div'); - document.body.append(container); - container.innerHTML = ReactDOMServer.renderToString(); - const root = ReactDOM.createRoot(container, {hydrate: true}); - act(() => { - root.render(); - }); - expect(Scheduler).toHaveYielded(['App', 'App']); - // The ID goes from not being used to being added to the page - act(() => { - _setShow(true); - }); - expect(Scheduler).toHaveYielded(['App', 'App']); - expect( - container.getElementsByTagName('span')[0].getAttribute('id'), - ).not.toBeNull(); - }); - - it('useOpaqueIdentifier: ID is not used during hydration but is used in an update in legacy', async () => { - let _setShow; - function App({unused}) { - Scheduler.unstable_yieldValue('App'); - const id = useOpaqueIdentifier(); - const [show, setShow] = useState(false); - _setShow = setShow; - return ( -
- {'Child One'} -
- ); - } - - const container = document.createElement('div'); - document.body.append(container); - container.innerHTML = ReactDOMServer.renderToString(); - ReactDOM.hydrate(, container); - expect(Scheduler).toHaveYielded(['App', 'App']); - // The ID goes from not being used to being added to the page - act(() => { - _setShow(true); - }); - expect(Scheduler).toHaveYielded(['App']); - expect( - container.getElementsByTagName('span')[0].getAttribute('id'), - ).not.toBeNull(); - }); - - it('useOpaqueIdentifier: flushSync', async () => { - let _setShow; - function App() { - const id = useOpaqueIdentifier(); - const [show, setShow] = useState(false); - _setShow = setShow; - return ( -
- {'Child One'} -
- ); - } - - const container = document.createElement('div'); - document.body.append(container); - container.innerHTML = ReactDOMServer.renderToString(); - const root = ReactDOM.createRoot(container, {hydrate: true}); - act(() => { - root.render(); - }); - - // The ID goes from not being used to being added to the page - act(() => { - ReactDOM.flushSync(() => { - _setShow(true); - }); - }); - expect( - container.getElementsByTagName('span')[0].getAttribute('id'), - ).not.toBeNull(); - }); - - it('useOpaqueIdentifier: children with id hydrates before other children if ID updates', async () => { - let _setShow; - - const child1Ref = React.createRef(); - const childWithIDRef = React.createRef(); - const setShowRef = React.createRef(); - - // RENAME THESE - function Child1() { - Scheduler.unstable_yieldValue('Child One'); - return {'Child One'}; - } - - function Child2() { - Scheduler.unstable_yieldValue('Child Two'); - return {'Child Two'}; - } - - const Children = React.memo(function Children() { - return ( - - - - - ); - }); - - function ChildWithID({parentID}) { - Scheduler.unstable_yieldValue('Child with ID'); - return ( - - {'Child with ID'} - - ); - } - - const ChildrenWithID = React.memo(function ChildrenWithID({parentID}) { - return ( - - - - ); - }); - - function App() { - const id = useOpaqueIdentifier(); - const [show, setShow] = useState(false); - _setShow = setShow; - return ( -
- - - {show && ( - - {'Child Three'} - - )} -
- ); - } - - const container = document.createElement('div'); - container.innerHTML = ReactDOMServer.renderToString(); - expect(Scheduler).toHaveYielded([ - 'Child One', - 'Child Two', - 'Child with ID', - ]); - expect(container.textContent).toEqual('Child OneChild TwoChild with ID'); - - const serverId = container - .getElementsByTagName('span')[2] - .getAttribute('id'); - expect(serverId).not.toBeNull(); - - const root = ReactDOM.createRoot(container, {hydrate: true}); - root.render(); - expect(Scheduler).toHaveYielded([]); - - //Hydrate just child one before updating state - expect(Scheduler).toFlushAndYieldThrough(['Child One']); - expect(child1Ref.current).toBe(null); - expect(Scheduler).toHaveYielded([]); - - act(() => { - _setShow(true); - - // State update should trigger the ID to update, which changes the props - // of ChildWithID. This should cause ChildWithID to hydrate before Children - - expect(Scheduler).toFlushAndYieldThrough([ - 'Child with ID', - // Fallbacks are immediately committed in TestUtils version - // of act - // 'Child with ID', - // 'Child with ID', - 'Child One', - 'Child Two', - ]); - - expect(child1Ref.current).toBe(null); - expect(childWithIDRef.current).toEqual( - container.getElementsByTagName('span')[2], - ); - - expect(setShowRef.current).toEqual( - container.getElementsByTagName('span')[3], - ); - - expect(childWithIDRef.current.getAttribute('id')).toEqual( - setShowRef.current.getAttribute('aria-labelledby'), - ); - expect(childWithIDRef.current.getAttribute('id')).not.toEqual(serverId); - }); - - // Children hydrates after ChildWithID - expect(child1Ref.current).toBe(container.getElementsByTagName('span')[0]); - - Scheduler.unstable_flushAll(); - - expect(Scheduler).toHaveYielded([]); - }); - - it('useOpaqueIdentifier: IDs match when part of the DOM tree is server rendered and part is client rendered', async () => { - let suspend = true; - let resolve; - const promise = new Promise(resolvePromise => (resolve = resolvePromise)); - - function Child({text}) { - if (suspend) { - throw promise; - } else { - return text; - } - } - - function RenderedChild() { - useEffect(() => { - Scheduler.unstable_yieldValue('Child did commit'); - }); - return null; - } - - function App() { - const id = useOpaqueIdentifier(); - useEffect(() => { - Scheduler.unstable_yieldValue('Did commit'); - }); - return ( -
-
Child One
- - -
- -
-
-
- ); - } - - const container = document.createElement('div'); - document.body.appendChild(container); - - container.innerHTML = ReactDOMServer.renderToString(); - - suspend = true; - const root = ReactDOM.createRoot(container, {hydrate: true}); - await act(async () => { - root.render(); - }); - jest.runAllTimers(); - expect(Scheduler).toHaveYielded(['Child did commit', 'Did commit']); - expect(Scheduler).toFlushAndYield([]); - - const serverId = container.children[0].children[0].getAttribute('id'); - expect(container.children[0].children.length).toEqual(1); - expect( - container.children[0].children[0].getAttribute('id'), - ).not.toBeNull(); - - await act(async () => { - suspend = false; - resolve(); - await promise; - }); - - expect(Scheduler).toHaveYielded(['Child did commit', 'Did commit']); - expect(Scheduler).toFlushAndYield([]); - jest.runAllTimers(); - - expect(container.children[0].children.length).toEqual(2); - expect(container.children[0].children[0].getAttribute('id')).toEqual( - container.children[0].children[1].getAttribute('id'), - ); - expect(container.children[0].children[0].getAttribute('id')).not.toEqual( - serverId, - ); - expect( - container.children[0].children[0].getAttribute('id'), - ).not.toBeNull(); - }); - - it('useOpaqueIdentifier warn when there is a hydration error', async () => { - function Child({appId}) { - return
; - } - function App() { - const id = useOpaqueIdentifier(); - return ; - } - - const container = document.createElement('div'); - document.body.appendChild(container); - - // This is the wrong HTML string - container.innerHTML = ''; - ReactDOM.createRoot(container, {hydrate: true}).render(); - expect(() => Scheduler.unstable_flushAll()).toErrorDev( - [ - 'Warning: An error occurred during hydration. The server HTML was replaced with client content in
.', - 'Warning: Expected server HTML to contain a matching
in
.', - ], - {withoutStack: 1}, - ); - }); - - it('useOpaqueIdentifier: IDs match when part of the DOM tree is server rendered and part is client rendered', async () => { - let suspend = true; - - function Child({text}) { - if (suspend) { - throw new Promise(() => {}); - } else { - return text; - } - } - - function RenderedChild() { - useEffect(() => { - Scheduler.unstable_yieldValue('Child did commit'); - }); - return null; - } - - function App() { - const id = useOpaqueIdentifier(); - useEffect(() => { - Scheduler.unstable_yieldValue('Did commit'); - }); - return ( -
-
Child One
- - -
- -
-
-
- ); - } - - const container = document.createElement('div'); - document.body.appendChild(container); - - container.innerHTML = ReactDOMServer.renderToString(); - - suspend = false; - const root = ReactDOM.createRoot(container, {hydrate: true}); - await act(async () => { - root.render(); - }); - jest.runAllTimers(); - expect(Scheduler).toHaveYielded([ - 'Child did commit', - 'Did commit', - 'Child did commit', - 'Did commit', - ]); - expect(Scheduler).toFlushAndYield([]); - - expect(container.children[0].children.length).toEqual(2); - expect(container.children[0].children[0].getAttribute('id')).toEqual( - container.children[0].children[1].getAttribute('id'), - ); - expect( - container.children[0].children[0].getAttribute('id'), - ).not.toBeNull(); - }); - - it('useOpaqueIdentifier warn when there is a hydration error', async () => { - function Child({appId}) { - return
; - } - function App() { - const id = useOpaqueIdentifier(); - return ; - } - - const container = document.createElement('div'); - document.body.appendChild(container); - - // This is the wrong HTML string - container.innerHTML = ''; - ReactDOM.createRoot(container, {hydrate: true}).render(); - expect(() => Scheduler.unstable_flushAll()).toErrorDev( - [ - 'Warning: An error occurred during hydration. The server HTML was replaced with client content in
.', - 'Warning: Expected server HTML to contain a matching
in
.', - ], - {withoutStack: 1}, - ); - }); - - it('useOpaqueIdentifier warns when there is a hydration error and we are using ID as a string', async () => { - function Child({appId}) { - return
; - } - function App() { - const id = useOpaqueIdentifier(); - return ; - } - - const container = document.createElement('div'); - document.body.appendChild(container); - - // This is the wrong HTML string - container.innerHTML = ''; - ReactDOM.createRoot(container, {hydrate: true}).render(); - expect(() => Scheduler.unstable_flushAll()).toErrorDev( - [ - 'Warning: The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. Do not read the value directly.', - 'Warning: An error occurred during hydration. The server HTML was replaced with client content in
.', - ], - {withoutStack: 1}, - ); - }); - - it('useOpaqueIdentifier warns when there is a hydration error and we are using ID as a string', async () => { - function Child({appId}) { - return
; - } - function App() { - const id = useOpaqueIdentifier(); - return ; - } - - const container = document.createElement('div'); - document.body.appendChild(container); - - // This is the wrong HTML string - container.innerHTML = ''; - ReactDOM.createRoot(container, {hydrate: true}).render(); - expect(() => Scheduler.unstable_flushAll()).toErrorDev( - [ - 'Warning: The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. Do not read the value directly.', - 'Warning: An error occurred during hydration. The server HTML was replaced with client content in
.', - ], - {withoutStack: 1}, - ); - }); - - it('useOpaqueIdentifier warns if you try to use the result as a string in a child component', async () => { - function Child({appId}) { - return
; - } - function App() { - const id = useOpaqueIdentifier(); - return ; - } - - const container = document.createElement('div'); - document.body.appendChild(container); - - container.innerHTML = ReactDOMServer.renderToString(); - ReactDOM.createRoot(container, {hydrate: true}).render(); - expect(() => Scheduler.unstable_flushAll()).toErrorDev( - [ - 'Warning: The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. Do not read the value directly.', - 'Warning: An error occurred during hydration. The server HTML was replaced with client content in
.', - ], - {withoutStack: 1}, - ); - }); - - it('useOpaqueIdentifier warns if you try to use the result as a string', async () => { - function App() { - const id = useOpaqueIdentifier(); - return
; - } - - const container = document.createElement('div'); - document.body.appendChild(container); - - container.innerHTML = ReactDOMServer.renderToString(); - ReactDOM.createRoot(container, {hydrate: true}).render(); - expect(() => Scheduler.unstable_flushAll()).toErrorDev( - [ - 'Warning: The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. Do not read the value directly.', - 'Warning: An error occurred during hydration. The server HTML was replaced with client content in
.', - ], - {withoutStack: 1}, - ); - }); - - it('useOpaqueIdentifier warns if you try to use the result as a string in a child component wrapped in a Suspense', async () => { - function Child({appId}) { - return
; - } - function App() { - const id = useOpaqueIdentifier(); - return ( - - - - ); - } - - const container = document.createElement('div'); - document.body.appendChild(container); - - container.innerHTML = ReactDOMServer.renderToString(); - - ReactDOM.createRoot(container, {hydrate: true}).render(); - - if (gate(flags => flags.deferRenderPhaseUpdateToNextBatch)) { - expect(() => Scheduler.unstable_flushAll()).toErrorDev([ - 'The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. ' + - 'Do not read the value directly.', - ]); - } else { - // This error isn't surfaced to the user; only the warning is. - // The error is just the mechanism that restarts the render. - expect(() => - expect(() => Scheduler.unstable_flushAll()).toThrow( - 'The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. ' + - 'Do not read the value directly.', - ), - ).toErrorDev([ - 'The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. ' + - 'Do not read the value directly.', - ]); - } - }); - - it('useOpaqueIdentifier warns if you try to add the result as a number in a child component wrapped in a Suspense', async () => { - function Child({appId}) { - return
; - } - function App() { - const [show] = useState(false); - const id = useOpaqueIdentifier(); - return ( - - {show &&
} - - - ); - } - - const container = document.createElement('div'); - document.body.appendChild(container); - - container.innerHTML = ReactDOMServer.renderToString(); - - ReactDOM.createRoot(container, {hydrate: true}).render(); - - if (gate(flags => flags.deferRenderPhaseUpdateToNextBatch)) { - expect(() => Scheduler.unstable_flushAll()).toErrorDev([ - 'The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. ' + - 'Do not read the value directly.', - ]); - } else { - // This error isn't surfaced to the user; only the warning is. - // The error is just the mechanism that restarts the render. - expect(() => - expect(() => Scheduler.unstable_flushAll()).toThrow( - 'The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. ' + - 'Do not read the value directly.', - ), - ).toErrorDev([ - 'The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. ' + - 'Do not read the value directly.', - ]); - } - }); - - it('useOpaqueIdentifier with two opaque identifiers on the same page', () => { - let _setShow; - - function App() { - const id1 = useOpaqueIdentifier(); - const id2 = useOpaqueIdentifier(); - const [show, setShow] = useState(true); - _setShow = setShow; - - return ( -
- - {show ? ( - {'Child'} - ) : ( - {'Child'} - )} - - {'test'} -
- ); - } - - const container = document.createElement('div'); - document.body.appendChild(container); - - container.innerHTML = ReactDOMServer.renderToString(); - - const serverID = container - .getElementsByTagName('span')[0] - .getAttribute('id'); - expect(serverID).not.toBeNull(); - expect( - container - .getElementsByTagName('span')[1] - .getAttribute('aria-labelledby'), - ).toEqual(serverID); - - ReactDOM.createRoot(container, {hydrate: true}).render(); - jest.runAllTimers(); - expect(Scheduler).toHaveYielded([]); - expect(Scheduler).toFlushAndYield([]); - - act(() => { - _setShow(false); - }); - - expect( - container - .getElementsByTagName('span')[1] - .getAttribute('aria-labelledby'), - ).toEqual(serverID); - expect( - container.getElementsByTagName('span')[0].getAttribute('id'), - ).not.toEqual(serverID); - expect( - container.getElementsByTagName('span')[0].getAttribute('id'), - ).not.toBeNull(); - }); - - it('useOpaqueIdentifier with multiple ids in nested components', async () => { - function DivWithId({id, children}) { - return
{children}
; - } - - let setShowMore; - function App() { - const outerId = useOpaqueIdentifier(); - const innerId = useOpaqueIdentifier(); - const [showMore, _setShowMore] = useState(false); - setShowMore = _setShowMore; - return showMore ? ( - - - - ) : null; - } - - const container = document.createElement('div'); - container.innerHTML = ReactDOMServer.renderToString(); - - await act(async () => { - ReactDOM.hydrateRoot(container, ); - }); - - // Show additional content that wasn't part of the initial server- - // rendered repsonse. - await act(async () => { - setShowMore(true); - }); - const [div1, div2] = container.getElementsByTagName('div'); - expect(typeof div1.getAttribute('id')).toBe('string'); - expect(typeof div2.getAttribute('id')).toBe('string'); - }); - }); }); diff --git a/packages/react-dom/src/client/DOMPropertyOperations.js b/packages/react-dom/src/client/DOMPropertyOperations.js index 30fed05aed609..ad94f7ccb7026 100644 --- a/packages/react-dom/src/client/DOMPropertyOperations.js +++ b/packages/react-dom/src/client/DOMPropertyOperations.js @@ -21,7 +21,6 @@ import { enableTrustedTypesIntegration, } from 'shared/ReactFeatureFlags'; import {checkAttributeStringCoercion} from 'shared/CheckStringCoercion'; -import {isOpaqueHydratingObject} from './ReactDOMHostConfig'; import type {PropertyInfo} from '../shared/DOMProperty'; @@ -119,13 +118,6 @@ export function getValueForAttribute( if (!isAttributeNameSafe(name)) { return; } - - // If the object is an opaque reference ID, it's expected that - // the next prop is different than the server value, so just return - // expected - if (isOpaqueHydratingObject(expected)) { - return expected; - } if (!node.hasAttribute(name)) { return expected === undefined ? undefined : null; } diff --git a/packages/react-dom/src/client/ReactDOMComponent.js b/packages/react-dom/src/client/ReactDOMComponent.js index 2d347639eba45..aa9b0281e2f8d 100644 --- a/packages/react-dom/src/client/ReactDOMComponent.js +++ b/packages/react-dom/src/client/ReactDOMComponent.js @@ -68,7 +68,6 @@ import possibleStandardNames from '../shared/possibleStandardNames'; import {validateProperties as validateARIAProperties} from '../shared/ReactDOMInvalidARIAHook'; import {validateProperties as validateInputProperties} from '../shared/ReactDOMNullInputValuePropHook'; import {validateProperties as validateUnknownProperties} from '../shared/ReactDOMUnknownPropertyHook'; -import {REACT_OPAQUE_ID_TYPE} from 'shared/ReactSymbols'; import {enableTrustedTypesIntegration} from 'shared/ReactFeatureFlags'; import { @@ -775,15 +774,6 @@ export function diffProperties( // to update this element. updatePayload = []; } - } else if ( - typeof nextProp === 'object' && - nextProp !== null && - nextProp.$$typeof === REACT_OPAQUE_ID_TYPE - ) { - // If we encounter useOpaqueReference's opaque object, this means we are hydrating. - // In this case, call the opaque object's toString function which generates a new client - // ID so client and server IDs match and throws to rerender. - nextProp.toString(); } else { // For any other property we always add it to the queue and then we // filter it out using the allowed property list during the commit. diff --git a/packages/react-dom/src/client/ReactDOMHostConfig.js b/packages/react-dom/src/client/ReactDOMHostConfig.js index 8a5417ae49272..64fd789b7a1e8 100644 --- a/packages/react-dom/src/client/ReactDOMHostConfig.js +++ b/packages/react-dom/src/client/ReactDOMHostConfig.js @@ -59,7 +59,6 @@ import { } from '../shared/HTMLNodeType'; import dangerousStyleValue from '../shared/dangerousStyleValue'; -import {REACT_OPAQUE_ID_TYPE} from 'shared/ReactSymbols'; import {retryIfBlockedOn} from '../events/ReactDOMEventReplaying'; import { @@ -124,13 +123,6 @@ export type TimeoutHandle = TimeoutID; export type NoTimeout = -1; export type RendererInspectionConfig = $ReadOnly<{||}>; -export opaque type OpaqueIDType = - | string - | { - toString: () => string | void, - valueOf: () => string | void, - }; - type SelectionInformation = {| focusedElem: null | HTMLElement, selectionRange: mixed, @@ -1089,43 +1081,6 @@ export function getInstanceFromNode(node: HTMLElement): null | Object { return getClosestInstanceFromNode(node) || null; } -let clientId: number = 0; -export function makeClientId(): OpaqueIDType { - return 'r:' + (clientId++).toString(36); -} - -export function makeClientIdInDEV(warnOnAccessInDEV: () => void): OpaqueIDType { - const id = 'r:' + (clientId++).toString(36); - return { - toString() { - warnOnAccessInDEV(); - return id; - }, - valueOf() { - warnOnAccessInDEV(); - return id; - }, - }; -} - -export function isOpaqueHydratingObject(value: mixed): boolean { - return ( - value !== null && - typeof value === 'object' && - value.$$typeof === REACT_OPAQUE_ID_TYPE - ); -} - -export function makeOpaqueHydratingObject( - attemptToReadValue: () => void, -): OpaqueIDType { - return { - $$typeof: REACT_OPAQUE_ID_TYPE, - toString: attemptToReadValue, - valueOf: attemptToReadValue, - }; -} - export function preparePortalMount(portalInstance: Instance): void { listenToAllSupportedEvents(portalInstance); } diff --git a/packages/react-dom/src/server/ReactDOMServerFormatConfig.js b/packages/react-dom/src/server/ReactDOMServerFormatConfig.js index 2012392004e52..65a10974b0196 100644 --- a/packages/react-dom/src/server/ReactDOMServerFormatConfig.js +++ b/packages/react-dom/src/server/ReactDOMServerFormatConfig.js @@ -64,9 +64,7 @@ export type ResponseState = { placeholderPrefix: PrecomputedChunk, segmentPrefix: PrecomputedChunk, boundaryPrefix: string, - opaqueIdentifierPrefix: string, nextSuspenseID: number, - nextOpaqueID: number, sentCompleteSegmentFunction: boolean, sentCompleteBoundaryFunction: boolean, sentClientRenderFunction: boolean, // We allow the legacy renderer to extend this object. @@ -127,9 +125,7 @@ export function createResponseState( placeholderPrefix: stringToPrecomputedChunk(idPrefix + 'P:'), segmentPrefix: stringToPrecomputedChunk(idPrefix + 'S:'), boundaryPrefix: idPrefix + 'B:', - opaqueIdentifierPrefix: idPrefix + 'R:', nextSuspenseID: 0, - nextOpaqueID: 0, sentCompleteSegmentFunction: false, sentCompleteBoundaryFunction: false, sentClientRenderFunction: false, @@ -233,24 +229,6 @@ export function assignSuspenseBoundaryID( ); } -export type OpaqueIDType = string; - -export function makeServerID( - responseState: null | ResponseState, -): OpaqueIDType { - if (responseState === null) { - throw new Error( - 'Invalid hook call. Hooks can only be called inside of the body of a function component.', - ); - } - - // TODO: This is not deterministic since it's created during render. - return ( - responseState.opaqueIdentifierPrefix + - (responseState.nextOpaqueID++).toString(36) - ); -} - function encodeHTMLTextNode(text: string): string { return escapeTextForBrowser(text); } diff --git a/packages/react-dom/src/server/ReactDOMServerLegacyFormatConfig.js b/packages/react-dom/src/server/ReactDOMServerLegacyFormatConfig.js index 7373e71681761..5b0798b737129 100644 --- a/packages/react-dom/src/server/ReactDOMServerLegacyFormatConfig.js +++ b/packages/react-dom/src/server/ReactDOMServerLegacyFormatConfig.js @@ -34,9 +34,7 @@ export type ResponseState = { placeholderPrefix: PrecomputedChunk, segmentPrefix: PrecomputedChunk, boundaryPrefix: string, - opaqueIdentifierPrefix: string, nextSuspenseID: number, - nextOpaqueID: number, sentCompleteSegmentFunction: boolean, sentCompleteBoundaryFunction: boolean, sentClientRenderFunction: boolean, @@ -56,9 +54,7 @@ export function createResponseState( placeholderPrefix: responseState.placeholderPrefix, segmentPrefix: responseState.segmentPrefix, boundaryPrefix: responseState.boundaryPrefix, - opaqueIdentifierPrefix: responseState.opaqueIdentifierPrefix, nextSuspenseID: responseState.nextSuspenseID, - nextOpaqueID: responseState.nextOpaqueID, sentCompleteSegmentFunction: responseState.sentCompleteSegmentFunction, sentCompleteBoundaryFunction: responseState.sentCompleteBoundaryFunction, sentClientRenderFunction: responseState.sentClientRenderFunction, @@ -77,14 +73,12 @@ export function createRootFormatContext(): FormatContext { export type { FormatContext, SuspenseBoundaryID, - OpaqueIDType, } from './ReactDOMServerFormatConfig'; export { getChildFormatContext, UNINITIALIZED_SUSPENSE_BOUNDARY_ID, assignSuspenseBoundaryID, - makeServerID, pushStartInstance, pushEndInstance, pushStartCompletedSuspenseBoundary, diff --git a/packages/react-dom/src/server/ReactPartialRenderer.js b/packages/react-dom/src/server/ReactPartialRenderer.js index 479a828a46e1f..1902dc65690c8 100644 --- a/packages/react-dom/src/server/ReactPartialRenderer.js +++ b/packages/react-dom/src/server/ReactPartialRenderer.js @@ -82,10 +82,6 @@ import {validateProperties as validateInputProperties} from '../shared/ReactDOMN import {validateProperties as validateUnknownProperties} from '../shared/ReactDOMUnknownPropertyHook'; import hasOwnProperty from 'shared/hasOwnProperty'; -export type ServerOptions = { - identifierPrefix?: string, -}; - // Based on reading the React.Children implementation. TODO: type this somewhere? type ReactNode = string | number | ReactElement; type FlatReactChildren = Array; @@ -784,14 +780,7 @@ class ReactDOMServerRenderer { contextValueStack: Array; contextProviderStack: ?Array>; // DEV-only - uniqueID: number; - identifierPrefix: string; - - constructor( - children: mixed, - makeStaticMarkup: boolean, - options?: ServerOptions, - ) { + constructor(children: mixed, makeStaticMarkup: boolean) { const flatChildren = flattenTopLevelChildren(children); const topFrame: Frame = { @@ -820,10 +809,6 @@ class ReactDOMServerRenderer { this.contextStack = []; this.contextValueStack = []; - // useOpaqueIdentifier ID - this.uniqueID = 0; - this.identifierPrefix = (options && options.identifierPrefix) || ''; - if (__DEV__) { this.contextProviderStack = []; } diff --git a/packages/react-dom/src/server/ReactPartialRendererHooks.js b/packages/react-dom/src/server/ReactPartialRendererHooks.js index 26f2dd00ee0c6..2940c47bde46d 100644 --- a/packages/react-dom/src/server/ReactPartialRendererHooks.js +++ b/packages/react-dom/src/server/ReactPartialRendererHooks.js @@ -41,8 +41,6 @@ type Hook = {| next: Hook | null, |}; -type OpaqueIDType = string; - let currentlyRenderingComponent: Object | null = null; let firstWorkInProgressHook: Hook | null = null; let workInProgressHook: Hook | null = null; @@ -511,15 +509,7 @@ function useTransition(): [boolean, (callback: () => void) => void] { return [false, startTransition]; } -function useOpaqueIdentifier(): OpaqueIDType { - return ( - (currentPartialRenderer.identifierPrefix || '') + - 'R:' + - (currentPartialRenderer.uniqueID++).toString(36) - ); -} - -function useId(): OpaqueIDType { +function useId(): string { throw new Error('Not implemented.'); } @@ -552,7 +542,6 @@ export const Dispatcher: DispatcherType = { useDebugValue: noop, useDeferredValue, useTransition, - useOpaqueIdentifier, useId, // Subscriptions are not setup in a server environment. useMutableSource, diff --git a/packages/react-native-renderer/src/ReactFabricHostConfig.js b/packages/react-native-renderer/src/ReactFabricHostConfig.js index c71a9050211cf..727b782efd768 100644 --- a/packages/react-native-renderer/src/ReactFabricHostConfig.js +++ b/packages/react-native-renderer/src/ReactFabricHostConfig.js @@ -84,8 +84,6 @@ export type UpdatePayload = Object; export type TimeoutHandle = TimeoutID; export type NoTimeout = -1; -export type OpaqueIDType = void; - export type RendererInspectionConfig = $ReadOnly<{| // Deprecated. Replaced with getInspectorDataForViewAtPoint. getInspectorDataForViewTag?: (tag: number) => Object, @@ -512,24 +510,6 @@ export function getInstanceFromNode(node: any) { throw new Error('Not yet implemented.'); } -export function isOpaqueHydratingObject(value: mixed): boolean { - throw new Error('Not yet implemented'); -} - -export function makeOpaqueHydratingObject( - attemptToReadValue: () => void, -): OpaqueIDType { - throw new Error('Not yet implemented.'); -} - -export function makeClientId(): OpaqueIDType { - throw new Error('Not yet implemented'); -} - -export function makeClientIdInDEV(warnOnAccessInDEV: () => void): OpaqueIDType { - throw new Error('Not yet implemented'); -} - export function beforeActiveInstanceBlur(internalInstanceHandle: Object) { // noop } diff --git a/packages/react-native-renderer/src/ReactNativeHostConfig.js b/packages/react-native-renderer/src/ReactNativeHostConfig.js index 769f46e852a2f..10c5e37f41bcc 100644 --- a/packages/react-native-renderer/src/ReactNativeHostConfig.js +++ b/packages/react-native-renderer/src/ReactNativeHostConfig.js @@ -43,7 +43,6 @@ export type ChildSet = void; // Unused export type TimeoutHandle = TimeoutID; export type NoTimeout = -1; -export type OpaqueIDType = void; export type RendererInspectionConfig = $ReadOnly<{| // Deprecated. Replaced with getInspectorDataForViewAtPoint. @@ -499,24 +498,6 @@ export function getInstanceFromNode(node: any) { throw new Error('Not yet implemented.'); } -export function isOpaqueHydratingObject(value: mixed): boolean { - throw new Error('Not yet implemented'); -} - -export function makeOpaqueHydratingObject( - attemptToReadValue: () => void, -): OpaqueIDType { - throw new Error('Not yet implemented.'); -} - -export function makeClientId(): OpaqueIDType { - throw new Error('Not yet implemented'); -} - -export function makeClientIdInDEV(warnOnAccessInDEV: () => void): OpaqueIDType { - throw new Error('Not yet implemented'); -} - export function beforeActiveInstanceBlur(internalInstanceHandle: Object) { // noop } diff --git a/packages/react-native-renderer/src/server/ReactNativeServerFormatConfig.js b/packages/react-native-renderer/src/server/ReactNativeServerFormatConfig.js index 3daa48f420b16..5b12d810bbe0f 100644 --- a/packages/react-native-renderer/src/server/ReactNativeServerFormatConfig.js +++ b/packages/react-native-renderer/src/server/ReactNativeServerFormatConfig.js @@ -59,14 +59,12 @@ SUSPENSE_UPDATE_TO_CLIENT_RENDER[0] = SUSPENSE_UPDATE_TO_CLIENT_RENDER_TAG; // Per response, export type ResponseState = { nextSuspenseID: number, - nextOpaqueID: number, }; // Allows us to keep track of what we've already written so we can refer back to it. export function createResponseState(): ResponseState { return { nextSuspenseID: 0, - nextOpaqueID: 0, }; } @@ -109,21 +107,6 @@ export function assignSuspenseBoundaryID( return responseState.nextSuspenseID++; } -export type OpaqueIDType = number; - -export function makeServerID( - responseState: null | ResponseState, -): OpaqueIDType { - if (responseState === null) { - throw new Error( - 'Invalid hook call. Hooks can only be called inside of the body of a function component.', - ); - } - - // TODO: This is not deterministic since it's created during render. - return responseState.nextOpaqueID++; -} - const RAW_TEXT = stringToPrecomputedChunk('RCTRawText'); export function pushTextInstance( diff --git a/packages/react-noop-renderer/src/ReactNoopServer.js b/packages/react-noop-renderer/src/ReactNoopServer.js index efcf727eac896..d217b16e50bfd 100644 --- a/packages/react-noop-renderer/src/ReactNoopServer.js +++ b/packages/react-noop-renderer/src/ReactNoopServer.js @@ -53,8 +53,6 @@ type Destination = { const POP = Buffer.from('/', 'utf8'); -let opaqueID = 0; - const ReactNoopServer = ReactFizzServer({ scheduleWork(callback: () => void) { callback(); @@ -88,10 +86,6 @@ const ReactNoopServer = ReactFizzServer({ return {state: 'pending', children: []}; }, - makeServerID(): number { - return opaqueID++; - }, - getChildFormatContext(): null { return null; }, diff --git a/packages/react-reconciler/src/ReactFiberHooks.new.js b/packages/react-reconciler/src/ReactFiberHooks.new.js index a1d7009a85a43..1238d673725ac 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.new.js +++ b/packages/react-reconciler/src/ReactFiberHooks.new.js @@ -17,7 +17,6 @@ import type {Fiber, Dispatcher, HookType} from './ReactInternalTypes'; import type {Lanes, Lane} from './ReactFiberLane.new'; import type {HookFlags} from './ReactHookEffectTags'; import type {FiberRoot} from './ReactInternalTypes'; -import type {OpaqueIDType} from './ReactFiberHostConfig'; import type {Cache} from './ReactFiberCacheComponent.new'; import type {Flags} from './ReactFiberFlags'; @@ -95,18 +94,12 @@ import { checkIfWorkInProgressReceivedUpdate, } from './ReactFiberBeginWork.new'; import {getIsHydrating} from './ReactFiberHydrationContext.new'; -import { - makeClientId, - makeClientIdInDEV, - makeOpaqueHydratingObject, -} from './ReactFiberHostConfig'; import { getWorkInProgressVersion, markSourceAsDirty, setWorkInProgressVersion, warnAboutMultipleRenderersDEV, } from './ReactMutableSource.new'; -import {getIsRendering} from './ReactCurrentFiber'; import {logStateUpdateScheduled} from './DebugTracing'; import {markStateUpdateScheduled} from './SchedulingProfiler'; import {createCache, CacheContext} from './ReactFiberCacheComponent.new'; @@ -139,10 +132,8 @@ export type UpdateQueue = {| |}; let didWarnAboutMismatchedHooksForComponent; -let didWarnAboutUseOpaqueIdentifier; let didWarnUncachedGetSnapshot; if (__DEV__) { - didWarnAboutUseOpaqueIdentifier = {}; didWarnAboutMismatchedHooksForComponent = new Set(); } @@ -2045,94 +2036,6 @@ export function getIsUpdatingOpaqueValueInRenderPhaseInDEV(): boolean | void { } } -function warnOnOpaqueIdentifierAccessInDEV(fiber) { - if (__DEV__) { - // TODO: Should warn in effects and callbacks, too - const name = getComponentNameFromFiber(fiber) || 'Unknown'; - if (getIsRendering() && !didWarnAboutUseOpaqueIdentifier[name]) { - console.error( - 'The object passed back from useOpaqueIdentifier is meant to be ' + - 'passed through to attributes only. Do not read the ' + - 'value directly.', - ); - didWarnAboutUseOpaqueIdentifier[name] = true; - } - } -} - -function mountOpaqueIdentifier(): OpaqueIDType | void { - const makeId = __DEV__ - ? makeClientIdInDEV.bind( - null, - warnOnOpaqueIdentifierAccessInDEV.bind(null, currentlyRenderingFiber), - ) - : makeClientId; - - if (getIsHydrating()) { - let didUpgrade = false; - const fiber = currentlyRenderingFiber; - const readValue = () => { - if (!didUpgrade) { - // Only upgrade once. This works even inside the render phase because - // the update is added to a shared queue, which outlasts the - // in-progress render. - didUpgrade = true; - if (__DEV__) { - isUpdatingOpaqueValueInRenderPhase = true; - setId(makeId()); - isUpdatingOpaqueValueInRenderPhase = false; - warnOnOpaqueIdentifierAccessInDEV(fiber); - } else { - setId(makeId()); - } - } - - throw new Error( - 'The object passed back from useOpaqueIdentifier is meant to be ' + - 'passed through to attributes only. Do not read the value directly.', - ); - }; - const id = makeOpaqueHydratingObject(readValue); - - const setId = mountState(id)[1]; - - if ((currentlyRenderingFiber.mode & ConcurrentMode) === NoMode) { - if ( - __DEV__ && - enableStrictEffects && - (currentlyRenderingFiber.mode & StrictEffectsMode) === NoMode - ) { - currentlyRenderingFiber.flags |= MountPassiveDevEffect | PassiveEffect; - } else { - currentlyRenderingFiber.flags |= PassiveEffect; - } - pushEffect( - HookHasEffect | HookPassive, - () => { - setId(makeId()); - }, - undefined, - null, - ); - } - return id; - } else { - const id = makeId(); - mountState(id); - return id; - } -} - -function updateOpaqueIdentifier(): OpaqueIDType | void { - const id = updateState(undefined)[0]; - return id; -} - -function rerenderOpaqueIdentifier(): OpaqueIDType | void { - const id = rerenderState(undefined)[0]; - return id; -} - function mountId(): string { const hook = mountWorkInProgressHook(); @@ -2481,7 +2384,6 @@ export const ContextOnlyDispatcher: Dispatcher = { useTransition: throwInvalidHookError, useMutableSource: throwInvalidHookError, useSyncExternalStore: throwInvalidHookError, - useOpaqueIdentifier: throwInvalidHookError, useId: throwInvalidHookError, unstable_isNewReconciler: enableNewReconciler, @@ -2510,7 +2412,6 @@ const HooksDispatcherOnMount: Dispatcher = { useTransition: mountTransition, useMutableSource: mountMutableSource, useSyncExternalStore: mountSyncExternalStore, - useOpaqueIdentifier: mountOpaqueIdentifier, useId: mountId, unstable_isNewReconciler: enableNewReconciler, @@ -2539,7 +2440,6 @@ const HooksDispatcherOnUpdate: Dispatcher = { useTransition: updateTransition, useMutableSource: updateMutableSource, useSyncExternalStore: updateSyncExternalStore, - useOpaqueIdentifier: updateOpaqueIdentifier, useId: updateId, unstable_isNewReconciler: enableNewReconciler, @@ -2568,7 +2468,6 @@ const HooksDispatcherOnRerender: Dispatcher = { useTransition: rerenderTransition, useMutableSource: updateMutableSource, useSyncExternalStore: mountSyncExternalStore, - useOpaqueIdentifier: rerenderOpaqueIdentifier, useId: updateId, unstable_isNewReconciler: enableNewReconciler, @@ -2736,11 +2635,6 @@ if (__DEV__) { mountHookTypesDev(); return mountSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); }, - useOpaqueIdentifier(): OpaqueIDType | void { - currentHookNameInDev = 'useOpaqueIdentifier'; - mountHookTypesDev(); - return mountOpaqueIdentifier(); - }, useId(): string { currentHookNameInDev = 'useId'; mountHookTypesDev(); @@ -2883,11 +2777,6 @@ if (__DEV__) { updateHookTypesDev(); return mountSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); }, - useOpaqueIdentifier(): OpaqueIDType | void { - currentHookNameInDev = 'useOpaqueIdentifier'; - updateHookTypesDev(); - return mountOpaqueIdentifier(); - }, useId(): string { currentHookNameInDev = 'useId'; updateHookTypesDev(); @@ -3030,11 +2919,6 @@ if (__DEV__) { updateHookTypesDev(); return updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); }, - useOpaqueIdentifier(): OpaqueIDType | void { - currentHookNameInDev = 'useOpaqueIdentifier'; - updateHookTypesDev(); - return updateOpaqueIdentifier(); - }, useId(): string { currentHookNameInDev = 'useId'; updateHookTypesDev(); @@ -3178,11 +3062,6 @@ if (__DEV__) { updateHookTypesDev(); return updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); }, - useOpaqueIdentifier(): OpaqueIDType | void { - currentHookNameInDev = 'useOpaqueIdentifier'; - updateHookTypesDev(); - return rerenderOpaqueIdentifier(); - }, useId(): string { currentHookNameInDev = 'useId'; updateHookTypesDev(); @@ -3341,12 +3220,6 @@ if (__DEV__) { mountHookTypesDev(); return mountSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); }, - useOpaqueIdentifier(): OpaqueIDType | void { - currentHookNameInDev = 'useOpaqueIdentifier'; - warnInvalidHookAccess(); - mountHookTypesDev(); - return mountOpaqueIdentifier(); - }, useId(): string { currentHookNameInDev = 'useId'; warnInvalidHookAccess(); @@ -3506,12 +3379,6 @@ if (__DEV__) { updateHookTypesDev(); return updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); }, - useOpaqueIdentifier(): OpaqueIDType | void { - currentHookNameInDev = 'useOpaqueIdentifier'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return updateOpaqueIdentifier(); - }, useId(): string { currentHookNameInDev = 'useId'; warnInvalidHookAccess(); @@ -3672,12 +3539,6 @@ if (__DEV__) { updateHookTypesDev(); return updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); }, - useOpaqueIdentifier(): OpaqueIDType | void { - currentHookNameInDev = 'useOpaqueIdentifier'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return rerenderOpaqueIdentifier(); - }, useId(): string { currentHookNameInDev = 'useId'; warnInvalidHookAccess(); diff --git a/packages/react-reconciler/src/ReactFiberHooks.old.js b/packages/react-reconciler/src/ReactFiberHooks.old.js index 167698271dbac..3b0b87cb1ab7c 100644 --- a/packages/react-reconciler/src/ReactFiberHooks.old.js +++ b/packages/react-reconciler/src/ReactFiberHooks.old.js @@ -17,7 +17,6 @@ import type {Fiber, Dispatcher, HookType} from './ReactInternalTypes'; import type {Lanes, Lane} from './ReactFiberLane.old'; import type {HookFlags} from './ReactHookEffectTags'; import type {FiberRoot} from './ReactInternalTypes'; -import type {OpaqueIDType} from './ReactFiberHostConfig'; import type {Cache} from './ReactFiberCacheComponent.old'; import type {Flags} from './ReactFiberFlags'; @@ -95,18 +94,12 @@ import { checkIfWorkInProgressReceivedUpdate, } from './ReactFiberBeginWork.old'; import {getIsHydrating} from './ReactFiberHydrationContext.old'; -import { - makeClientId, - makeClientIdInDEV, - makeOpaqueHydratingObject, -} from './ReactFiberHostConfig'; import { getWorkInProgressVersion, markSourceAsDirty, setWorkInProgressVersion, warnAboutMultipleRenderersDEV, } from './ReactMutableSource.old'; -import {getIsRendering} from './ReactCurrentFiber'; import {logStateUpdateScheduled} from './DebugTracing'; import {markStateUpdateScheduled} from './SchedulingProfiler'; import {createCache, CacheContext} from './ReactFiberCacheComponent.old'; @@ -139,10 +132,8 @@ export type UpdateQueue = {| |}; let didWarnAboutMismatchedHooksForComponent; -let didWarnAboutUseOpaqueIdentifier; let didWarnUncachedGetSnapshot; if (__DEV__) { - didWarnAboutUseOpaqueIdentifier = {}; didWarnAboutMismatchedHooksForComponent = new Set(); } @@ -2045,94 +2036,6 @@ export function getIsUpdatingOpaqueValueInRenderPhaseInDEV(): boolean | void { } } -function warnOnOpaqueIdentifierAccessInDEV(fiber) { - if (__DEV__) { - // TODO: Should warn in effects and callbacks, too - const name = getComponentNameFromFiber(fiber) || 'Unknown'; - if (getIsRendering() && !didWarnAboutUseOpaqueIdentifier[name]) { - console.error( - 'The object passed back from useOpaqueIdentifier is meant to be ' + - 'passed through to attributes only. Do not read the ' + - 'value directly.', - ); - didWarnAboutUseOpaqueIdentifier[name] = true; - } - } -} - -function mountOpaqueIdentifier(): OpaqueIDType | void { - const makeId = __DEV__ - ? makeClientIdInDEV.bind( - null, - warnOnOpaqueIdentifierAccessInDEV.bind(null, currentlyRenderingFiber), - ) - : makeClientId; - - if (getIsHydrating()) { - let didUpgrade = false; - const fiber = currentlyRenderingFiber; - const readValue = () => { - if (!didUpgrade) { - // Only upgrade once. This works even inside the render phase because - // the update is added to a shared queue, which outlasts the - // in-progress render. - didUpgrade = true; - if (__DEV__) { - isUpdatingOpaqueValueInRenderPhase = true; - setId(makeId()); - isUpdatingOpaqueValueInRenderPhase = false; - warnOnOpaqueIdentifierAccessInDEV(fiber); - } else { - setId(makeId()); - } - } - - throw new Error( - 'The object passed back from useOpaqueIdentifier is meant to be ' + - 'passed through to attributes only. Do not read the value directly.', - ); - }; - const id = makeOpaqueHydratingObject(readValue); - - const setId = mountState(id)[1]; - - if ((currentlyRenderingFiber.mode & ConcurrentMode) === NoMode) { - if ( - __DEV__ && - enableStrictEffects && - (currentlyRenderingFiber.mode & StrictEffectsMode) === NoMode - ) { - currentlyRenderingFiber.flags |= MountPassiveDevEffect | PassiveEffect; - } else { - currentlyRenderingFiber.flags |= PassiveEffect; - } - pushEffect( - HookHasEffect | HookPassive, - () => { - setId(makeId()); - }, - undefined, - null, - ); - } - return id; - } else { - const id = makeId(); - mountState(id); - return id; - } -} - -function updateOpaqueIdentifier(): OpaqueIDType | void { - const id = updateState(undefined)[0]; - return id; -} - -function rerenderOpaqueIdentifier(): OpaqueIDType | void { - const id = rerenderState(undefined)[0]; - return id; -} - function mountId(): string { const hook = mountWorkInProgressHook(); @@ -2481,7 +2384,6 @@ export const ContextOnlyDispatcher: Dispatcher = { useTransition: throwInvalidHookError, useMutableSource: throwInvalidHookError, useSyncExternalStore: throwInvalidHookError, - useOpaqueIdentifier: throwInvalidHookError, useId: throwInvalidHookError, unstable_isNewReconciler: enableNewReconciler, @@ -2510,7 +2412,6 @@ const HooksDispatcherOnMount: Dispatcher = { useTransition: mountTransition, useMutableSource: mountMutableSource, useSyncExternalStore: mountSyncExternalStore, - useOpaqueIdentifier: mountOpaqueIdentifier, useId: mountId, unstable_isNewReconciler: enableNewReconciler, @@ -2539,7 +2440,6 @@ const HooksDispatcherOnUpdate: Dispatcher = { useTransition: updateTransition, useMutableSource: updateMutableSource, useSyncExternalStore: updateSyncExternalStore, - useOpaqueIdentifier: updateOpaqueIdentifier, useId: updateId, unstable_isNewReconciler: enableNewReconciler, @@ -2568,7 +2468,6 @@ const HooksDispatcherOnRerender: Dispatcher = { useTransition: rerenderTransition, useMutableSource: updateMutableSource, useSyncExternalStore: mountSyncExternalStore, - useOpaqueIdentifier: rerenderOpaqueIdentifier, useId: updateId, unstable_isNewReconciler: enableNewReconciler, @@ -2736,11 +2635,6 @@ if (__DEV__) { mountHookTypesDev(); return mountSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); }, - useOpaqueIdentifier(): OpaqueIDType | void { - currentHookNameInDev = 'useOpaqueIdentifier'; - mountHookTypesDev(); - return mountOpaqueIdentifier(); - }, useId(): string { currentHookNameInDev = 'useId'; mountHookTypesDev(); @@ -2883,11 +2777,6 @@ if (__DEV__) { updateHookTypesDev(); return mountSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); }, - useOpaqueIdentifier(): OpaqueIDType | void { - currentHookNameInDev = 'useOpaqueIdentifier'; - updateHookTypesDev(); - return mountOpaqueIdentifier(); - }, useId(): string { currentHookNameInDev = 'useId'; updateHookTypesDev(); @@ -3030,11 +2919,6 @@ if (__DEV__) { updateHookTypesDev(); return updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); }, - useOpaqueIdentifier(): OpaqueIDType | void { - currentHookNameInDev = 'useOpaqueIdentifier'; - updateHookTypesDev(); - return updateOpaqueIdentifier(); - }, useId(): string { currentHookNameInDev = 'useId'; updateHookTypesDev(); @@ -3178,11 +3062,6 @@ if (__DEV__) { updateHookTypesDev(); return updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); }, - useOpaqueIdentifier(): OpaqueIDType | void { - currentHookNameInDev = 'useOpaqueIdentifier'; - updateHookTypesDev(); - return rerenderOpaqueIdentifier(); - }, useId(): string { currentHookNameInDev = 'useId'; updateHookTypesDev(); @@ -3341,12 +3220,6 @@ if (__DEV__) { mountHookTypesDev(); return mountSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); }, - useOpaqueIdentifier(): OpaqueIDType | void { - currentHookNameInDev = 'useOpaqueIdentifier'; - warnInvalidHookAccess(); - mountHookTypesDev(); - return mountOpaqueIdentifier(); - }, useId(): string { currentHookNameInDev = 'useId'; warnInvalidHookAccess(); @@ -3506,12 +3379,6 @@ if (__DEV__) { updateHookTypesDev(); return updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); }, - useOpaqueIdentifier(): OpaqueIDType | void { - currentHookNameInDev = 'useOpaqueIdentifier'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return updateOpaqueIdentifier(); - }, useId(): string { currentHookNameInDev = 'useId'; warnInvalidHookAccess(); @@ -3672,12 +3539,6 @@ if (__DEV__) { updateHookTypesDev(); return updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); }, - useOpaqueIdentifier(): OpaqueIDType | void { - currentHookNameInDev = 'useOpaqueIdentifier'; - warnInvalidHookAccess(); - updateHookTypesDev(); - return rerenderOpaqueIdentifier(); - }, useId(): string { currentHookNameInDev = 'useId'; warnInvalidHookAccess(); diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js index e35e5515bb0bf..71bf8175105d6 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js @@ -474,8 +474,7 @@ export function scheduleUpdateOnFiber( // if the update originates from user space (with the exception of local // hook updates, which are handled differently and don't reach this // function), but there are some internal React features that use this as - // an implementation detail, like selective hydration - // and useOpaqueIdentifier. + // an implementation detail, like selective hydration. warnAboutRenderPhaseUpdatesInDEV(fiber); // Track lanes that were updated during the render phase @@ -898,12 +897,10 @@ function recoverFromConcurrentError(root, errorRetryLanes) { exitStatus === RootErrored && workInProgressRootRenderPhaseUpdatedLanes !== NoLanes ) { - // There was a render phase update during this render. This was likely a - // useOpaqueIdentifier hook upgrading itself to a client ID. Try rendering - // again. This time, the component will use a client ID and will proceed - // without throwing. If multiple IDs upgrade as a result of the same - // update, we will have to do multiple render passes. To protect against - // an inifinite loop, eventually we'll give up. + // There was a render phase update during this render. Some internal React + // implementation details may use this as a trick to schedule another + // render pass. To protect against an inifinite loop, eventually + // we'll give up. continue; } break; diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js index cde46adb01ab0..8be4630dc079d 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js @@ -474,8 +474,7 @@ export function scheduleUpdateOnFiber( // if the update originates from user space (with the exception of local // hook updates, which are handled differently and don't reach this // function), but there are some internal React features that use this as - // an implementation detail, like selective hydration - // and useOpaqueIdentifier. + // an implementation detail, like selective hydration. warnAboutRenderPhaseUpdatesInDEV(fiber); // Track lanes that were updated during the render phase @@ -898,12 +897,10 @@ function recoverFromConcurrentError(root, errorRetryLanes) { exitStatus === RootErrored && workInProgressRootRenderPhaseUpdatedLanes !== NoLanes ) { - // There was a render phase update during this render. This was likely a - // useOpaqueIdentifier hook upgrading itself to a client ID. Try rendering - // again. This time, the component will use a client ID and will proceed - // without throwing. If multiple IDs upgrade as a result of the same - // update, we will have to do multiple render passes. To protect against - // an inifinite loop, eventually we'll give up. + // There was a render phase update during this render. Some internal React + // implementation details may use this as a trick to schedule another + // render pass. To protect against an inifinite loop, eventually + // we'll give up. continue; } break; diff --git a/packages/react-reconciler/src/ReactInternalTypes.js b/packages/react-reconciler/src/ReactInternalTypes.js index 9dac4f40aad53..db964103836da 100644 --- a/packages/react-reconciler/src/ReactInternalTypes.js +++ b/packages/react-reconciler/src/ReactInternalTypes.js @@ -43,7 +43,6 @@ export type HookType = | 'useTransition' | 'useMutableSource' | 'useSyncExternalStore' - | 'useOpaqueIdentifier' | 'useId' | 'useCacheRefresh'; @@ -317,7 +316,6 @@ export type Dispatcher = {| getSnapshot: () => T, getServerSnapshot?: () => T, ): T, - useOpaqueIdentifier(): any, useId(): string, useCacheRefresh?: () => (?() => T, ?T) => void, diff --git a/packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js b/packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js index cc842bd16d22f..55ce08b45450b 100644 --- a/packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js @@ -1916,14 +1916,14 @@ describe('ReactIncrementalErrorHandling', () => { }); it("does not infinite loop if there's a render phase update in the same render as an error", async () => { - // useOpaqueIdentifier uses an render phase update as an implementation - // detail. When an error is accompanied by a render phase update, we assume - // that it comes from useOpaqueIdentifier, because render phase updates - // triggered from userspace are not allowed (we log a warning). So we keep - // attempting to recover until no more opaque identifiers need to be - // upgraded. However, we should give up after some point to prevent an - // infinite loop in the case where there is (by accident) a render phase - // triggered from userspace. + // Some React features may schedule a render phase update as an + // implementation detail. When an error is accompanied by a render phase + // update, we assume that it comes from React internals, because render + // phase updates triggered from userspace are not allowed (we log a + // warning). So we keep attempting to recover until no more opaque + // identifiers need to be upgraded. However, we should give up after some + // point to prevent an infinite loop in the case where there is (by + // accident) a render phase triggered from userspace. spyOnDev(console, 'error'); diff --git a/packages/react-reconciler/src/forks/ReactFiberHostConfig.custom.js b/packages/react-reconciler/src/forks/ReactFiberHostConfig.custom.js index 29d4fd7e79893..6535d8d3fdec3 100644 --- a/packages/react-reconciler/src/forks/ReactFiberHostConfig.custom.js +++ b/packages/react-reconciler/src/forks/ReactFiberHostConfig.custom.js @@ -38,7 +38,6 @@ export opaque type ChildSet = mixed; // eslint-disable-line no-undef export opaque type TimeoutHandle = mixed; // eslint-disable-line no-undef export opaque type NoTimeout = mixed; // eslint-disable-line no-undef export opaque type RendererInspectionConfig = mixed; // eslint-disable-line no-undef -export opaque type OpaqueIDType = mixed; export type EventResponder = any; export const getPublicInstance = $$$hostConfig.getPublicInstance; @@ -62,11 +61,6 @@ export const supportsMutation = $$$hostConfig.supportsMutation; export const supportsPersistence = $$$hostConfig.supportsPersistence; export const supportsHydration = $$$hostConfig.supportsHydration; export const getInstanceFromNode = $$$hostConfig.getInstanceFromNode; -export const isOpaqueHydratingObject = $$$hostConfig.isOpaqueHydratingObject; -export const makeOpaqueHydratingObject = - $$$hostConfig.makeOpaqueHydratingObject; -export const makeClientId = $$$hostConfig.makeClientId; -export const makeClientIdInDEV = $$$hostConfig.makeClientIdInDEV; export const beforeActiveInstanceBlur = $$$hostConfig.beforeActiveInstanceBlur; export const afterActiveInstanceBlur = $$$hostConfig.afterActiveInstanceBlur; export const preparePortalMount = $$$hostConfig.preparePortalMount; diff --git a/packages/react-server/src/ReactFizzHooks.js b/packages/react-server/src/ReactFizzHooks.js index bf1a2d56e0f93..7b31e6b37ed9e 100644 --- a/packages/react-server/src/ReactFizzHooks.js +++ b/packages/react-server/src/ReactFizzHooks.js @@ -16,14 +16,12 @@ import type { ReactContext, } from 'shared/ReactTypes'; -import type {ResponseState, OpaqueIDType} from './ReactServerFormatConfig'; +import type {ResponseState} from './ReactServerFormatConfig'; import type {Task} from './ReactFizzServer'; import {readContext as readContextImpl} from './ReactFizzNewContext'; import {getTreeId} from './ReactFizzTreeContext'; -import {makeServerID} from './ReactServerFormatConfig'; - import {enableCache} from 'shared/ReactFeatureFlags'; import is from 'shared/objectIs'; @@ -509,10 +507,6 @@ function useTransition(): [boolean, (callback: () => void) => void] { return [false, unsupportedStartTransition]; } -function useOpaqueIdentifier(): OpaqueIDType { - return makeServerID(currentResponseState); -} - function useId(): string { const task: Task = (currentlyRenderingTask: any); const treeId = getTreeId(task.treeContext); @@ -559,7 +553,6 @@ export const Dispatcher: DispatcherType = { useDebugValue: noop, useDeferredValue, useTransition, - useOpaqueIdentifier, useId, // Subscriptions are not setup in a server environment. useMutableSource, diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index bba34065cc268..418fbb8241a66 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -845,7 +845,6 @@ const Dispatcher: DispatcherType = { useLayoutEffect: (unsupportedHook: any), useImperativeHandle: (unsupportedHook: any), useEffect: (unsupportedHook: any), - useOpaqueIdentifier: (unsupportedHook: any), useId: (unsupportedHook: any), useMutableSource: (unsupportedHook: any), useSyncExternalStore: (unsupportedHook: any), diff --git a/packages/react-server/src/forks/ReactServerFormatConfig.custom.js b/packages/react-server/src/forks/ReactServerFormatConfig.custom.js index 8cfe59ce1628c..f246ddab8b5bf 100644 --- a/packages/react-server/src/forks/ReactServerFormatConfig.custom.js +++ b/packages/react-server/src/forks/ReactServerFormatConfig.custom.js @@ -28,7 +28,6 @@ export opaque type Destination = mixed; // eslint-disable-line no-undef export opaque type ResponseState = mixed; export opaque type FormatContext = mixed; export opaque type SuspenseBoundaryID = mixed; -export opaque type OpaqueIDType = mixed; export const isPrimaryRenderer = false; @@ -36,7 +35,6 @@ export const getChildFormatContext = $$$hostConfig.getChildFormatContext; export const UNINITIALIZED_SUSPENSE_BOUNDARY_ID = $$$hostConfig.UNINITIALIZED_SUSPENSE_BOUNDARY_ID; export const assignSuspenseBoundaryID = $$$hostConfig.assignSuspenseBoundaryID; -export const makeServerID = $$$hostConfig.makeServerID; export const pushTextInstance = $$$hostConfig.pushTextInstance; export const pushStartInstance = $$$hostConfig.pushStartInstance; export const pushEndInstance = $$$hostConfig.pushEndInstance; diff --git a/packages/react-suspense-test-utils/src/ReactSuspenseTestUtils.js b/packages/react-suspense-test-utils/src/ReactSuspenseTestUtils.js index 731124ea51593..d75f1f7c9b453 100644 --- a/packages/react-suspense-test-utils/src/ReactSuspenseTestUtils.js +++ b/packages/react-suspense-test-utils/src/ReactSuspenseTestUtils.js @@ -42,7 +42,6 @@ export function waitForSuspense(fn: () => T): Promise { useDebugValue: unsupported, useDeferredValue: unsupported, useTransition: unsupported, - useOpaqueIdentifier: unsupported, useId: unsupported, useMutableSource: unsupported, useSyncExternalStore: unsupported, diff --git a/packages/react-test-renderer/src/ReactTestHostConfig.js b/packages/react-test-renderer/src/ReactTestHostConfig.js index 6cdadabe281a5..503b08efaf20b 100644 --- a/packages/react-test-renderer/src/ReactTestHostConfig.js +++ b/packages/react-test-renderer/src/ReactTestHostConfig.js @@ -7,7 +7,6 @@ * @flow */ -import {REACT_OPAQUE_ID_TYPE} from 'shared/ReactSymbols'; import isArray from 'shared/isArray'; import {DefaultEventPriority} from 'react-reconciler/src/ReactEventPriorities'; @@ -40,12 +39,6 @@ export type ChildSet = void; // Unused export type TimeoutHandle = TimeoutID; export type NoTimeout = -1; export type EventResponder = any; -export opaque type OpaqueIDType = - | string - | { - toString: () => string | void, - valueOf: () => string | void, - }; export type RendererInspectionConfig = $ReadOnly<{||}>; @@ -298,43 +291,6 @@ export function getInstanceFromNode(mockNode: Object) { return null; } -let clientId: number = 0; -export function makeClientId(): OpaqueIDType { - return 'c_' + (clientId++).toString(36); -} - -export function makeClientIdInDEV(warnOnAccessInDEV: () => void): OpaqueIDType { - const id = 'c_' + (clientId++).toString(36); - return { - toString() { - warnOnAccessInDEV(); - return id; - }, - valueOf() { - warnOnAccessInDEV(); - return id; - }, - }; -} - -export function isOpaqueHydratingObject(value: mixed): boolean { - return ( - value !== null && - typeof value === 'object' && - value.$$typeof === REACT_OPAQUE_ID_TYPE - ); -} - -export function makeOpaqueHydratingObject( - attemptToReadValue: () => void, -): OpaqueIDType { - return { - $$typeof: REACT_OPAQUE_ID_TYPE, - toString: attemptToReadValue, - valueOf: attemptToReadValue, - }; -} - export function beforeActiveInstanceBlur(internalInstanceHandle: Object) { // noop } diff --git a/packages/react/index.classic.fb.js b/packages/react/index.classic.fb.js index a2e678c580b2e..70bbc6112a56c 100644 --- a/packages/react/index.classic.fb.js +++ b/packages/react/index.classic.fb.js @@ -40,7 +40,6 @@ export { unstable_getCacheSignal, unstable_getCacheForType, unstable_useCacheRefresh, - unstable_useOpaqueIdentifier, unstable_useId, useCallback, useContext, diff --git a/packages/react/index.experimental.js b/packages/react/index.experimental.js index 20c22828583f5..bc3f4883bdb3d 100644 --- a/packages/react/index.experimental.js +++ b/packages/react/index.experimental.js @@ -36,7 +36,6 @@ export { unstable_getCacheSignal, unstable_getCacheForType, unstable_useCacheRefresh, - unstable_useOpaqueIdentifier, unstable_useId, useCallback, useContext, diff --git a/packages/react/index.js b/packages/react/index.js index 3108c06c55284..a27507524e50b 100644 --- a/packages/react/index.js +++ b/packages/react/index.js @@ -61,7 +61,6 @@ export { unstable_getCacheSignal, unstable_getCacheForType, unstable_useCacheRefresh, - unstable_useOpaqueIdentifier, unstable_useId, useCallback, useContext, diff --git a/packages/react/index.modern.fb.js b/packages/react/index.modern.fb.js index eef99fdabf2a4..922acfc158109 100644 --- a/packages/react/index.modern.fb.js +++ b/packages/react/index.modern.fb.js @@ -39,7 +39,6 @@ export { unstable_getCacheSignal, unstable_getCacheForType, unstable_useCacheRefresh, - unstable_useOpaqueIdentifier, unstable_useId, useCallback, useContext, diff --git a/packages/react/index.stable.js b/packages/react/index.stable.js index 517dc3f8fb2db..eb9673efd16ad 100644 --- a/packages/react/index.stable.js +++ b/packages/react/index.stable.js @@ -29,7 +29,6 @@ export { lazy, memo, startTransition, - unstable_useOpaqueIdentifier, unstable_useId, useCallback, useContext, diff --git a/packages/react/src/React.js b/packages/react/src/React.js index 868538c83f59b..5fe5b4d96b609 100644 --- a/packages/react/src/React.js +++ b/packages/react/src/React.js @@ -52,7 +52,6 @@ import { useState, useTransition, useDeferredValue, - useOpaqueIdentifier, useId, useCacheRefresh, } from './ReactHooks'; @@ -127,7 +126,6 @@ export { REACT_CACHE_TYPE as unstable_Cache, // enableScopeAPI REACT_SCOPE_TYPE as unstable_Scope, - useOpaqueIdentifier as unstable_useOpaqueIdentifier, useId as unstable_useId, act, }; diff --git a/packages/react/src/ReactHooks.js b/packages/react/src/ReactHooks.js index 1f987de1671ba..12a5350083f38 100644 --- a/packages/react/src/ReactHooks.js +++ b/packages/react/src/ReactHooks.js @@ -14,7 +14,6 @@ import type { MutableSourceSubscribeFn, ReactContext, } from 'shared/ReactTypes'; -import type {OpaqueIDType} from 'react-reconciler/src/ReactFiberHostConfig'; import ReactCurrentDispatcher from './ReactCurrentDispatcher'; @@ -169,11 +168,6 @@ export function useDeferredValue(value: T): T { return dispatcher.useDeferredValue(value); } -export function useOpaqueIdentifier(): OpaqueIDType | void { - const dispatcher = resolveDispatcher(); - return dispatcher.useOpaqueIdentifier(); -} - export function useId(): string { const dispatcher = resolveDispatcher(); return dispatcher.useId(); diff --git a/packages/react/unstable-shared-subset.experimental.js b/packages/react/unstable-shared-subset.experimental.js index d556400750b4c..7a9352f30199a 100644 --- a/packages/react/unstable-shared-subset.experimental.js +++ b/packages/react/unstable-shared-subset.experimental.js @@ -27,7 +27,6 @@ export { unstable_DebugTracingMode, unstable_getCacheSignal, unstable_getCacheForType, - unstable_useOpaqueIdentifier, unstable_useId, useCallback, useContext, diff --git a/packages/shared/CheckStringCoercion.js b/packages/shared/CheckStringCoercion.js index 9db9e7145ad4f..305572f8462c7 100644 --- a/packages/shared/CheckStringCoercion.js +++ b/packages/shared/CheckStringCoercion.js @@ -7,8 +7,6 @@ * @flow */ -import {REACT_OPAQUE_ID_TYPE} from 'shared/ReactSymbols'; - /* * The `'' + value` pattern (used in in perf-sensitive code) throws for Symbol * and Temporal.* types. See https://github.com/facebook/react/pull/22064. @@ -35,16 +33,6 @@ function typeName(value: mixed): string { // $FlowFixMe only called in DEV, so void return is not possible. function willCoercionThrow(value: mixed): boolean { if (__DEV__) { - if ( - value !== null && - typeof value === 'object' && - value.$$typeof === REACT_OPAQUE_ID_TYPE - ) { - // OpaqueID type is expected to throw, so React will handle it. Not sure if - // it's expected that string coercion will throw, but we'll assume it's OK. - // See https://github.com/facebook/react/issues/20127. - return; - } try { testStringCoercion(value); return false; diff --git a/packages/shared/ReactSymbols.js b/packages/shared/ReactSymbols.js index c2fcba6324410..a50a06b150169 100644 --- a/packages/shared/ReactSymbols.js +++ b/packages/shared/ReactSymbols.js @@ -26,7 +26,6 @@ export let REACT_SUSPENSE_LIST_TYPE = 0xead8; export let REACT_MEMO_TYPE = 0xead3; export let REACT_LAZY_TYPE = 0xead4; export let REACT_SCOPE_TYPE = 0xead7; -export let REACT_OPAQUE_ID_TYPE = 0xeae0; export let REACT_DEBUG_TRACING_MODE_TYPE = 0xeae1; export let REACT_OFFSCREEN_TYPE = 0xeae2; export let REACT_LEGACY_HIDDEN_TYPE = 0xeae3; @@ -47,7 +46,6 @@ if (typeof Symbol === 'function' && Symbol.for) { REACT_MEMO_TYPE = symbolFor('react.memo'); REACT_LAZY_TYPE = symbolFor('react.lazy'); REACT_SCOPE_TYPE = symbolFor('react.scope'); - REACT_OPAQUE_ID_TYPE = symbolFor('react.opaque.id'); REACT_DEBUG_TRACING_MODE_TYPE = symbolFor('react.debug_trace_mode'); REACT_OFFSCREEN_TYPE = symbolFor('react.offscreen'); REACT_LEGACY_HIDDEN_TYPE = symbolFor('react.legacy_hidden'); diff --git a/scripts/error-codes/codes.json b/scripts/error-codes/codes.json index aab5d62476cc3..93391eb309d49 100644 --- a/scripts/error-codes/codes.json +++ b/scripts/error-codes/codes.json @@ -342,7 +342,7 @@ "352": "React Lazy Components are not yet supported on the server.", "353": "A server block should never encode any other slots. This is a bug in React.", "354": "getInspectorDataForViewAtPoint() is not available in production.", - "355": "The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. Do not read the value directly.", + "355": "The object passed back from useOpaqueIdentifier is meant to be passed through to attributes only. Do not read the value directly. (TODO: This feature was never released so we should be able to remove this error from the map.)", "356": "Could not read the cache.", "357": "The current renderer does not support React Scopes. This error is likely caused by a bug in React. Please file an issue.", "358": "Invalid update priority: %s. This is a bug in React.",