Skip to content

Commit

Permalink
Handle reentrancy in server renderer
Browse files Browse the repository at this point in the history
Context stack should be per server renderer instance.
  • Loading branch information
acdlite committed Dec 14, 2017
1 parent 4aaae58 commit b6a95c3
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,59 @@ describe('ReactDOMServer', () => {
expect(results).toEqual([2, 1, 3, 1]);
});

it('renders context API, reentrancy', () => {
const Context = React.unstable_createContext(0);

function Provider(props) {
return Context.provide(props.value, props.children);
}

function Consumer(props) {
return Context.consume(value => {
return 'Result: ' + value;
});
}

let reentrantMarkup;
function Reentrant() {
reentrantMarkup = ReactDOMServer.renderToString(
<App value={1} reentrant={false} />,
);
return null;
}

const Indirection = React.Fragment;

function App(props) {
return (
<Provider value={props.value}>
{props.reentrant && <Reentrant />}
<Provider value={2}>
<Consumer />
</Provider>
<Indirection>
<Indirection>
<Consumer />
<Provider value={3}>
<Consumer />
</Provider>
</Indirection>
</Indirection>
<Consumer />
</Provider>
);
}

const markup = ReactDOMServer.renderToString(
<App value={1} reentrant={true} />,
);
// Extract the numbers rendered by the consumers
const results = markup.match(/\d+/g).map(Number);
const reentrantResults = reentrantMarkup.match(/\d+/g).map(Number);
expect(results).toEqual([2, 1, 3, 1]);
expect(reentrantResults).toEqual([2, 1, 3, 1]);
});

it('renders components with different batching strategies', () => {
class StaticComponent extends React.Component {
render() {
Expand Down
65 changes: 36 additions & 29 deletions packages/react-dom/src/server/ReactPartialRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,33 +123,6 @@ if (__DEV__) {
};
}

// Context (new API)
let providerStack: Array<ReactProvider<any>> = []; // Stack of provider objects
let index = -1;

export function pushProvider<T>(provider: ReactProvider<T>): void {
index += 1;
providerStack[index] = provider;
const context: ReactContext<any> = provider.type.context;
context.currentProvider = provider;
}

export function popProvider<T>(provider: ReactProvider<T>): void {
if (__DEV__) {
warning(index > -1 && provider === providerStack[index], 'Unexpected pop.');
}
// $FlowFixMe - Intentionally unsound
providerStack[index] = null;
index -= 1;
const context: ReactContext<any> = provider.type.context;
if (index < 0) {
context.currentProvider = null;
} else {
const previousProvider = providerStack[index];
context.currentProvider = previousProvider;
}
}

let didWarnDefaultInputValue = false;
let didWarnDefaultChecked = false;
let didWarnDefaultSelectValue = false;
Expand Down Expand Up @@ -565,6 +538,9 @@ class ReactDOMServerRenderer {
previousWasTextNode: boolean;
makeStaticMarkup: boolean;

providerStack: Array<ReactProvider<any>>;
providerIndex: number;

constructor(children: mixed, makeStaticMarkup: boolean) {
const flatChildren = flattenTopLevelChildren(children);

Expand All @@ -586,6 +562,37 @@ class ReactDOMServerRenderer {
this.currentSelectValue = null;
this.previousWasTextNode = false;
this.makeStaticMarkup = makeStaticMarkup;

// Context (new API)
this.providerStack = []; // Stack of provider objects
this.providerIndex = -1;
}

pushProvider<T>(provider: ReactProvider<T>): void {
this.providerIndex += 1;
this.providerStack[this.providerIndex] = provider;
const context: ReactContext<any> = provider.type.context;
context.currentProvider = provider;
}

popProvider<T>(provider: ReactProvider<T>): void {
if (__DEV__) {
warning(
this.providerIndex > -1 &&
provider === this.providerStack[this.providerIndex],
'Unexpected pop.',
);
}
// $FlowFixMe - Intentionally unsound
this.providerStack[this.providerIndex] = null;
this.providerIndex -= 1;
const context: ReactContext<any> = provider.type.context;
if (this.providerIndex < 0) {
context.currentProvider = null;
} else {
const previousProvider = this.providerStack[this.providerIndex];
context.currentProvider = previousProvider;
}
}

read(bytes: number): string | null {
Expand Down Expand Up @@ -615,7 +622,7 @@ class ReactDOMServerRenderer {
frame.type.type.$$typeof === REACT_PROVIDER_TYPE
) {
const provider: ReactProvider<any> = (frame.type: any);
popProvider(provider);
this.popProvider(provider);
}
continue;
}
Expand Down Expand Up @@ -743,7 +750,7 @@ class ReactDOMServerRenderer {
((frame: any): FrameDev).debugElementStack = [];
}

pushProvider(provider);
this.pushProvider(provider);

this.stack.push(frame);
return '';
Expand Down

0 comments on commit b6a95c3

Please sign in to comment.