Skip to content

Commit 8171a0f

Browse files
authored
Merge pull request #8 from mikestead/fix/merge-child-context
Fix issue where child context was replacing instead of augmenting parent context
2 parents e56ff70 + 2f591fb commit 8171a0f

File tree

2 files changed

+106
-1
lines changed

2 files changed

+106
-1
lines changed

src/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ export default function renderToString(vnode, context, opts, inner) {
156156
rendered = c.render(c.props, c.state, c.context);
157157

158158
if (c.getChildContext) {
159-
context = c.getChildContext();
159+
context = assign(assign({}, context), c.getChildContext());
160160
}
161161
}
162162

test/render.js

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,111 @@ describe('render', () => {
229229
.to.have.been.calledOnce
230230
.and.to.have.been.calledBefore(Test.prototype.render);
231231
});
232+
233+
it('should pass context to grandchildren', () => {
234+
const CONTEXT = { a:'a' };
235+
const PROPS = { b:'b' };
236+
237+
class Outer extends Component {
238+
getChildContext() {
239+
return CONTEXT;
240+
}
241+
render(props) {
242+
return <div><Inner {...props} /></div>;
243+
}
244+
}
245+
spy(Outer.prototype, 'getChildContext');
246+
247+
class Inner extends Component {
248+
render(props, state, context) {
249+
return <div>{ context && context.a }</div>;
250+
}
251+
}
252+
spy(Inner.prototype, 'render');
253+
254+
render(<Outer />);
255+
256+
expect(Outer.prototype.getChildContext).to.have.been.calledOnce;
257+
expect(Inner.prototype.render).to.have.been.calledWith({}, {}, CONTEXT);
258+
259+
CONTEXT.foo = 'bar';
260+
render(<Outer {...PROPS} />);
261+
262+
expect(Outer.prototype.getChildContext).to.have.been.calledTwice;
263+
expect(Inner.prototype.render).to.have.been.calledWith(PROPS, {}, CONTEXT);
264+
});
265+
266+
it('should pass context to direct children', () => {
267+
const CONTEXT = { a:'a' };
268+
const PROPS = { b:'b' };
269+
270+
class Outer extends Component {
271+
getChildContext() {
272+
return CONTEXT;
273+
}
274+
render(props) {
275+
return <Inner {...props} />;
276+
}
277+
}
278+
spy(Outer.prototype, 'getChildContext');
279+
280+
class Inner extends Component {
281+
render(props, state, context) {
282+
return <div>{ context && context.a }</div>;
283+
}
284+
}
285+
spy(Inner.prototype, 'render');
286+
287+
render(<Outer />);
288+
289+
expect(Outer.prototype.getChildContext).to.have.been.calledOnce;
290+
expect(Inner.prototype.render).to.have.been.calledWith({}, {}, CONTEXT);
291+
292+
CONTEXT.foo = 'bar';
293+
render(<Outer {...PROPS} />);
294+
295+
expect(Outer.prototype.getChildContext).to.have.been.calledTwice;
296+
expect(Inner.prototype.render).to.have.been.calledWith(PROPS, {}, CONTEXT);
297+
298+
// make sure render() could make use of context.a
299+
expect(Inner.prototype.render).to.have.returned(match({ children:['a'] }));
300+
});
301+
302+
it('should preserve existing context properties when creating child contexts', () => {
303+
let outerContext = { outer:true },
304+
innerContext = { inner:true };
305+
class Outer extends Component {
306+
getChildContext() {
307+
return { outerContext };
308+
}
309+
render() {
310+
return <div><Inner /></div>;
311+
}
312+
}
313+
314+
class Inner extends Component {
315+
getChildContext() {
316+
return { innerContext };
317+
}
318+
render() {
319+
return <InnerMost />;
320+
}
321+
}
322+
323+
class InnerMost extends Component {
324+
render() {
325+
return <strong>test</strong>;
326+
}
327+
}
328+
329+
spy(Inner.prototype, 'render');
330+
spy(InnerMost.prototype, 'render');
331+
332+
render(<Outer />);
333+
334+
expect(Inner.prototype.render).to.have.been.calledWith({}, {}, { outerContext });
335+
expect(InnerMost.prototype.render).to.have.been.calledWith({}, {}, { outerContext, innerContext });
336+
});
232337
});
233338

234339
describe('High-order components', () => {

0 commit comments

Comments
 (0)