Skip to content
This repository was archived by the owner on Apr 13, 2023. It is now read-only.

Commit 99a846f

Browse files
authored
Restore getDataFromTree(rootElement, rootContext) API. (#2586)
In [email protected], the rootContext parameter was replaced by the custom renderFunction parameter, which was a breaking change: #2533 (comment) This commit restores the previous API, while providing an alternative API that is more flexible and more accurately named: getMarkupFromTree. If you want to specify a custom rendering function, you should now use getMarkupFromTree instead of getDataFromTree: import { getMarkupFromTree } from "react-apollo"; import { renderToString } from "react-dom/server"; getMarkupFromTree({ tree: <App/>, // optional; defaults to {} context: { ... }, // optional; defaults to renderToStaticMarkup renderFunction: renderToString, }).then(markup => { // Use the markup returned by the final rendering... }); Hopefully, since [email protected] was never assigned the `latest` tag on npm, we can push this change without another minor version bump. cc @evenchange4 @hwillson
1 parent cb225f5 commit 99a846f

File tree

4 files changed

+105
-38
lines changed

4 files changed

+105
-38
lines changed

Changelog.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,35 @@
2121
necessary anymore.
2222
[PR #2533](https://github.com/apollographql/react-apollo/pull/2533)
2323

24+
- Restore original `getDataFromTree(tree, context)` API, and introduce a
25+
new function called `getMarkupFromTree` to enable custom rendering
26+
functions:
27+
```typescript
28+
export default function getDataFromTree(
29+
tree: React.ReactNode,
30+
context: { [key: string]: any } = {},
31+
) {
32+
return getMarkupFromTree({
33+
tree,
34+
context,
35+
renderFunction: renderToStaticMarkup,
36+
});
37+
}
38+
39+
export type GetMarkupFromTreeOptions = {
40+
tree: React.ReactNode;
41+
context?: { [key: string]: any };
42+
renderFunction?: typeof renderToStaticMarkup;
43+
};
44+
45+
export function getMarkupFromTree({
46+
tree,
47+
context = {},
48+
renderFunction = renderToStaticMarkup,
49+
}: GetMarkupFromTreeOptions): Promise<string> {...}
50+
```
51+
[PR #2586](https://github.com/apollographql/react-apollo/pull/2586)
52+
2453
## 2.2.4 (October 2, 2018)
2554

2655
### Bug Fixes

examples/ssr/server/main.js

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { renderToString } from 'react-dom/server';
22
import { onPageLoad } from 'meteor/server-render';
33
import { ApolloClient } from 'apollo-client';
4-
import { getDataFromTree, ApolloProvider } from 'react-apollo';
4+
import { getMarkupFromTree, ApolloProvider } from 'react-apollo';
55
import { InMemoryCache } from 'apollo-cache-inmemory';
66
import { HttpLink } from 'apollo-link-http';
77
import { WebApp } from 'meteor/webapp';
@@ -27,12 +27,14 @@ export const render = async sink => {
2727
</ApolloProvider>
2828
);
2929

30-
const start = +new Date;
30+
const start = Date.now();
3131
// Load all data from local server
32-
await getDataFromTree(WrappedApp);
33-
const body = renderToString(WrappedApp);
34-
console.log("server rendering took", new Date - start, "ms");
35-
sink.renderIntoElementById('app', body);
32+
const markup = await getMarkupFromTree({
33+
tree: WrappedApp,
34+
renderFunction: renderToString,
35+
});
36+
console.log("server rendering took", Date.now() - start, "ms");
37+
sink.renderIntoElementById('app', markup);
3638
sink.appendToBody(`
3739
<script>
3840
window.__APOLLO_STATE__=${JSON.stringify(client.extract())};

src/getDataFromTree.ts

Lines changed: 45 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -84,46 +84,60 @@ export class RenderPromises {
8484
}
8585
}
8686

87-
class RenderPromisesProvider extends React.Component<{
88-
renderPromises: RenderPromises;
89-
}> {
90-
static childContextTypes = {
91-
renderPromises: PropTypes.object,
92-
};
93-
94-
getChildContext() {
95-
return {
96-
renderPromises: this.props.renderPromises,
97-
};
98-
}
99-
100-
render() {
101-
return this.props.children;
102-
}
87+
export default function getDataFromTree(
88+
tree: React.ReactNode,
89+
context: { [key: string]: any } = {},
90+
) {
91+
return getMarkupFromTree({
92+
tree,
93+
context,
94+
// If you need to configure this renderFunction, call getMarkupFromTree
95+
// directly instead of getDataFromTree.
96+
renderFunction: renderToStaticMarkup,
97+
});
10398
}
10499

105-
export default function getDataFromTree(
106-
rootElement: React.ReactNode,
100+
export type GetMarkupFromTreeOptions = {
101+
tree: React.ReactNode;
102+
context?: { [key: string]: any };
103+
renderFunction?: typeof renderToStaticMarkup;
104+
};
105+
106+
export function getMarkupFromTree({
107+
tree,
108+
context = {},
107109
// The rendering function is configurable! We use renderToStaticMarkup as
108110
// the default, because it's a little less expensive than renderToString,
109111
// and legacy usage of getDataFromTree ignores the return value anyway.
110112
renderFunction = renderToStaticMarkup,
111-
): Promise<string> {
113+
}: GetMarkupFromTreeOptions): Promise<string> {
112114
const renderPromises = new RenderPromises();
113115

114-
function process(): Promise<string> | string {
115-
const html = renderFunction(
116-
React.createElement(RenderPromisesProvider, {
117-
renderPromises,
118-
// Always re-render from the rootElement, even though it might seem
119-
// better to render the children of the component responsible for the
120-
// promise, because it is not possible to reconstruct the full context
121-
// of the original rendering (including all unknown context provider
122-
// elements) for a subtree of the orginal component tree.
123-
children: rootElement,
124-
})
125-
);
116+
class RenderPromisesProvider extends React.Component {
117+
static childContextTypes: { [key: string]: any } = {
118+
renderPromises: PropTypes.object,
119+
};
120+
121+
getChildContext() {
122+
return { ...context, renderPromises };
123+
}
124+
125+
render() {
126+
// Always re-render from the rootElement, even though it might seem
127+
// better to render the children of the component responsible for the
128+
// promise, because it is not possible to reconstruct the full context
129+
// of the original rendering (including all unknown context provider
130+
// elements) for a subtree of the orginal component tree.
131+
return tree;
132+
}
133+
}
126134

135+
Object.keys(context).forEach(key => {
136+
RenderPromisesProvider.childContextTypes[key] = PropTypes.any;
137+
});
138+
139+
function process(): Promise<string> | string {
140+
const html = renderFunction(React.createElement(RenderPromisesProvider));
127141
return renderPromises.hasPromises()
128142
? renderPromises.consumeAndAwaitPromises().then(process)
129143
: html;

test/client/getDataFromTree.test.tsx

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
ApolloProvider,
99
walkTree,
1010
getDataFromTree,
11+
getMarkupFromTree,
1112
DataValue,
1213
ChildProps,
1314
} from '../../src';
@@ -511,13 +512,34 @@ describe('SSR', () => {
511512
expect(markup).toMatch(/James/);
512513
});
513514

514-
await getDataFromTree(app, ReactDOM.renderToString).then(html => {
515+
await getMarkupFromTree({
516+
tree: app,
517+
renderFunction: ReactDOM.renderToString,
518+
}).then(html => {
515519
const markup = ReactDOM.renderToString(app);
516520
expect(markup).toEqual(html);
517521
expect(markup).toMatch(/James/);
518522
});
519523
});
520524

525+
it('should support passing a root context', () => {
526+
class Consumer extends React.Component {
527+
static contextTypes = {
528+
text: PropTypes.string.isRequired,
529+
};
530+
531+
render() {
532+
return <div>{this.context.text}</div>;
533+
}
534+
}
535+
536+
return getDataFromTree(<Consumer/>, {
537+
text: "oyez"
538+
}).then(html => {
539+
expect(html).toEqual('<div>oyez</div>');
540+
});
541+
});
542+
521543
it('should run through all of the queries (also defined via Query component) that want SSR', () => {
522544
const query = gql`
523545
{

0 commit comments

Comments
 (0)