diff --git a/src/create-context.js b/src/create-context.js index 36a17e936e..7daae35348 100644 --- a/src/create-context.js +++ b/src/create-context.js @@ -13,11 +13,6 @@ export function createContext(defaultValue) { _id: '__cC' + i++, _defaultValue: defaultValue, Consumer(props, context) { - - this.shouldComponentUpdate = function (_props, _state, _context) { - return _context !== context || props.children !== _props.children; - }; - return props.children(context); }, Provider(props) { @@ -27,14 +22,17 @@ export function createContext(defaultValue) { ctx[context._id] = this; return ctx; }; - this.shouldComponentUpdate = props => { - subs.some(c => { - // Check if still mounted - if (c._parentDom) { - c.context = props.value; - enqueueRender(c); - } - }); + this.shouldComponentUpdate = _props => { + if (props.value !== _props.value) { + ctx[context._id].props.value = _props.value; + subs.some(c => { + // Check if still mounted + if (c._parentDom) { + c.context = _props.value; + enqueueRender(c); + } + }); + } }; this.sub = (c) => { subs.push(c); diff --git a/test/browser/createContext.test.js b/test/browser/createContext.test.js index f44747230d..72812aa6c8 100644 --- a/test/browser/createContext.test.js +++ b/test/browser/createContext.test.js @@ -1,4 +1,4 @@ -import { setupRerender } from 'preact/test-utils'; +import { setupRerender, act } from 'preact/test-utils'; import { createElement as h, render, Component, createContext } from '../../src/index'; import { setupScratch, teardown } from '../_util/helpers'; import { Fragment } from '../../src'; @@ -355,6 +355,16 @@ describe('createContext', () => { const { Provider, Consumer } = createContext(); const CONTEXT = { i: 1 }; + class NoUpdate extends Component { + shouldComponentUpdate() { + return false; + } + + render() { + return this.props.children; + } + } + class Inner extends Component { render(props) { return
{props.i}
; @@ -363,22 +373,22 @@ describe('createContext', () => { sinon.spy(Inner.prototype, 'render'); - const Child = data => ; - render( - - {Child} - + + + {data => } + + , scratch ); render( - - {Child} - + + {data => } + , scratch ); @@ -387,20 +397,66 @@ describe('createContext', () => { expect(Inner.prototype.render).to.have.been.calledOnce.and.calledWithMatch(CONTEXT, {}, { ['__cC' + (ctxId - 1)]: {} }); expect(scratch.innerHTML).to.equal('
1
'); - render( - - - {Child} - - , - scratch - ); + act(() => { + render( + + + {data => } + + , + scratch + ); + }); // Rendered three times, should call 'Consumer' render two times expect(Inner.prototype.render).to.have.been.calledTwice.and.calledWithMatch({ i: 2 }, {}, { ['__cC' + (ctxId - 1)]: {} }); expect(scratch.innerHTML).to.equal('
2
'); }); + it('should allow for updates of props', () => { + let app; + const { Provider, Consumer } = createContext(); + class App extends Component { + constructor(props) { + super(props); + this.state = { + status: 'initial' + }; + + this.renderInner = this.renderInner.bind(this); + + app = this; + } + + renderInner(value) { + return

{value}: {this.state.status}

; + } + + render() { + return ( + + + {this.renderInner} + + + ); + } + } + + act(() => { + render(, scratch); + }); + + expect(scratch.innerHTML).to.equal('

value: initial

'); + + act(() => { + app.setState({ status: 'updated' }); + rerender(); + }); + + expect(scratch.innerHTML).to.equal('

value: updated

'); + }); + it('should re-render the consumer if the children change', () => { const { Provider, Consumer } = createContext(); const CONTEXT = { i: 1 }; @@ -413,23 +469,27 @@ describe('createContext', () => { sinon.spy(Inner.prototype, 'render'); - render( - - - {data => } - - , - scratch - ); + act(() => { - render( - - - {data => } - - , - scratch - ); + render( + + + {data => } + + , + scratch + ); + + // Not calling re-render since it's gonna get called with the same Consumer function + render( + + + {data => } + + , + scratch + ); + }); // Rendered twice, with two different children for consumer, should render twice expect(Inner.prototype.render).to.have.been.calledTwice;