Skip to content

Commit

Permalink
add new IDs for each each server renderer instance and prefixes to di…
Browse files Browse the repository at this point in the history
…stinguish between each server render
  • Loading branch information
lunaruan committed Apr 15, 2020
1 parent b04c7fa commit e8f6c8a
Show file tree
Hide file tree
Showing 10 changed files with 154 additions and 20 deletions.
2 changes: 1 addition & 1 deletion packages/react-art/src/ReactARTHostConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ export function makeClientIdInDEV(warnOnAccessInDEV: () => void): OpaqueIDType {
throw new Error('Not yet implemented');
}

export function makeServerId(): OpaqueIDType {
export function makeServerId(prefix: ?string, serverId: number): OpaqueIDType {
throw new Error('Not yet implemented');
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1008,6 +1008,91 @@ describe('ReactDOMServerHooks', () => {
);
});

it('useOpaqueIdentifier prefix works for server renderer and does not clash', async () => {
function ChildTwo({id}) {
return <div id={id}>Child Three</div>;
}
function App() {
const id = useOpaqueIdentifier();
const idTwo = useOpaqueIdentifier();

return (
<div>
<div aria-labelledby={id}>Chid One</div>
<ChildTwo id={id} />
<div aria-labelledby={idTwo}>Child Three</div>
<div id={idTwo}>Child Four</div>
</div>
);
}

const containerOne = document.createElement('div');
document.body.append(containerOne);

containerOne.innerHTML = ReactDOMServer.renderToString(<App />, {
prefix: 'one',
});

const containerTwo = document.createElement('div');
document.body.append(containerTwo);

containerTwo.innerHTML = ReactDOMServer.renderToString(<App />, {
prefix: '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: IDs match when, after hydration, a new component that uses the ID is rendered', async () => {
let _setShowDiv;
function App() {
Expand Down
5 changes: 2 additions & 3 deletions packages/react-dom/src/client/ReactDOMHostConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -1174,9 +1174,8 @@ export function makeClientIdInDEV(warnOnAccessInDEV: () => void): OpaqueIDType {
};
}

let serverId: number = 0;
export function makeServerId(): OpaqueIDType {
return 'R:' + (serverId++).toString(36);
export function makeServerId(prefix: ?string, serverId: number): OpaqueIDType {
return (prefix || '') + 'R:' + serverId.toString(36);
}

export function isOpaqueHydratingObject(value: mixed): boolean {
Expand Down
15 changes: 11 additions & 4 deletions packages/react-dom/src/server/ReactDOMNodeStreamRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import type {PartialRendererOptions} from './ReactPartialRenderer';

import {Readable} from 'stream';

Expand Down Expand Up @@ -36,15 +37,21 @@ class ReactMarkupReadableStream extends Readable {
* server.
* See https://reactjs.org/docs/react-dom-server.html#rendertonodestream
*/
export function renderToNodeStream(element) {
return new ReactMarkupReadableStream(element, false);
export function renderToNodeStream(
element,
options: PartialRendererOptions | void,
) {
return new ReactMarkupReadableStream(element, false, options);
}

/**
* Similar to renderToNodeStream, except this doesn't create extra DOM attributes
* such as data-react-id that React uses internally.
* See https://reactjs.org/docs/react-dom-server.html#rendertostaticnodestream
*/
export function renderToStaticNodeStream(element) {
return new ReactMarkupReadableStream(element, true);
export function renderToStaticNodeStream(
element,
options: PartialRendererOptions | void,
) {
return new ReactMarkupReadableStream(element, true, options);
}
17 changes: 12 additions & 5 deletions packages/react-dom/src/server/ReactDOMStringRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,19 @@
* LICENSE file in the root directory of this source tree.
*/

import type {PartialRendererOptions} from './ReactPartialRenderer';
import ReactPartialRenderer from './ReactPartialRenderer';

/**
* Render a ReactElement to its initial HTML. This should only be used on the
* server.
* See https://reactjs.org/docs/react-dom-server.html#rendertostring
*/
export function renderToString(element) {
const renderer = new ReactPartialRenderer(element, false);
export function renderToString(
element,
options?: void | PartialRendererOptions,
) {
const renderer = new ReactPartialRenderer(element, false, options);
try {
const markup = renderer.read(Infinity);
return markup;
Expand All @@ -27,10 +31,13 @@ export function renderToString(element) {
* such as data-react-id that React uses internally.
* See https://reactjs.org/docs/react-dom-server.html#rendertostaticmarkup
*/
export function renderToStaticMarkup(element) {
const renderer = new ReactPartialRenderer(element, true);
export function renderToStaticMarkup(
element,
options?: void | PartialRendererOptions,
) {
const renderer = new ReactPartialRenderer(element, true, options);
try {
const markup = renderer.read(Infinity);
const markup = renderer.read(Infinity, options);
return markup;
} finally {
renderer.destroy();
Expand Down
28 changes: 27 additions & 1 deletion packages/react-dom/src/server/ReactPartialRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ import {
Dispatcher,
currentThreadID,
setCurrentThreadID,
currentUniqueID,
setCurrentUniqueID,
uniqueIDPrefix,
setCurrentUniqueIDPrefix,
} from './ReactPartialRendererHooks';
import {
Namespaces,
Expand All @@ -78,6 +82,10 @@ import {validateProperties as validateARIAProperties} from '../shared/ReactDOMIn
import {validateProperties as validateInputProperties} from '../shared/ReactDOMNullInputValuePropHook';
import {validateProperties as validateUnknownProperties} from '../shared/ReactDOMUnknownPropertyHook';

export type PartialRendererOptions = {
prefix?: string,
};

// Based on reading the React.Children implementation. TODO: type this somewhere?
type ReactNode = string | number | ReactElement;
type FlatReactChildren = Array<null | ReactNode>;
Expand Down Expand Up @@ -725,7 +733,14 @@ class ReactDOMServerRenderer {
contextValueStack: Array<any>;
contextProviderStack: ?Array<ReactProvider<any>>; // DEV-only

constructor(children: mixed, makeStaticMarkup: boolean) {
uniqueID: number;
prefix: string;

constructor(
children: mixed,
makeStaticMarkup: boolean,
options?: PartialRendererOptions | void,
) {
const flatChildren = flattenTopLevelChildren(children);

const topFrame: Frame = {
Expand Down Expand Up @@ -753,6 +768,11 @@ class ReactDOMServerRenderer {
this.contextIndex = -1;
this.contextStack = [];
this.contextValueStack = [];

// useOpaqueIdentifier ID
this.uniqueID = 0;
this.prefix = (options && options.prefix) || '';

if (__DEV__) {
this.contextProviderStack = [];
}
Expand Down Expand Up @@ -838,6 +858,10 @@ class ReactDOMServerRenderer {

const prevThreadID = currentThreadID;
setCurrentThreadID(this.threadID);
const prevUniqueID = currentUniqueID;
setCurrentUniqueID(this.uniqueID);
const prevUniquePrefix = uniqueIDPrefix;
setCurrentUniqueIDPrefix(this.prefix);
const prevDispatcher = ReactCurrentDispatcher.current;
ReactCurrentDispatcher.current = Dispatcher;
try {
Expand Down Expand Up @@ -935,6 +959,8 @@ class ReactDOMServerRenderer {
} finally {
ReactCurrentDispatcher.current = prevDispatcher;
setCurrentThreadID(prevThreadID);
setCurrentUniqueID(prevUniqueID);
setCurrentUniqueIDPrefix(prevUniquePrefix);
}
}

Expand Down
13 changes: 12 additions & 1 deletion packages/react-dom/src/server/ReactPartialRendererHooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -495,8 +495,19 @@ function useTransition(
return [startTransition, false];
}

export let currentUniqueID: number = 0;
export let uniqueIDPrefix: string = '';

export function setCurrentUniqueIDPrefix(prefix: string) {
uniqueIDPrefix = prefix;
}

export function setCurrentUniqueID(id: number) {
currentUniqueID = id;
}

function useOpaqueIdentifier(): OpaqueIDType {
return makeServerId();
return makeServerId(uniqueIDPrefix, currentUniqueID++);
}

function useEvent(event: any): ReactDOMListenerMap {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ export function makeClientIdInDEV(warnOnAccessInDEV: () => void): OpaqueIDType {
throw new Error('Not yet implemented');
}

export function makeServerId(): OpaqueIDType {
export function makeServerId(prefix: ?string, serverId: number): OpaqueIDType {
throw new Error('Not yet implemented');
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,7 @@ export function makeClientIdInDEV(warnOnAccessInDEV: () => void): OpaqueIDType {
throw new Error('Not yet implemented');
}

export function makeServerId(): OpaqueIDType {
export function makeServerId(prefix: ?string, serverId: number): OpaqueIDType {
throw new Error('Not yet implemented');
}

Expand Down
5 changes: 2 additions & 3 deletions packages/react-test-renderer/src/ReactTestHostConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -407,9 +407,8 @@ export function makeClientIdInDEV(warnOnAccessInDEV: () => void): OpaqueIDType {
};
}

let serverId: number = 0;
export function makeServerId(): OpaqueIDType {
return 's_' + (serverId++).toString(36);
export function makeServerId(prefix: ?string, serverId: number): OpaqueIDType {
return (prefix || '') + 's_' + serverId.toString(36);
}

export function isOpaqueHydratingObject(value: mixed): boolean {
Expand Down

0 comments on commit e8f6c8a

Please sign in to comment.