Skip to content

Commit

Permalink
repurpose .update() to handle impure renders and props/state mutation
Browse files Browse the repository at this point in the history
  • Loading branch information
jwbay committed Oct 24, 2016
1 parent d6a51b7 commit 74f62b6
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 4 deletions.
1 change: 1 addition & 0 deletions docs/README.md
Expand Up @@ -67,6 +67,7 @@
* [text()](/docs/api/ShallowWrapper/text.md)
* [type()](/docs/api/ShallowWrapper/type.md)
* [unmount()](/docs/api/ShallowWrapper/unmount.md)
* [update()](/docs/api/ShallowWrapper/update.md)
* [Full DOM Rendering](/docs/api/mount.md)
* [at(index)](/docs/api/ReactWrapper/at.md)
* [contains(nodeOrNodes)](/docs/api/ReactWrapper/contains.md)
Expand Down
33 changes: 33 additions & 0 deletions docs/api/ShallowWrapper/update.md
@@ -0,0 +1,33 @@
# `.update() => Self`

Forces a re-render. Useful to run before checking the render output if something external
may be updating the state of the component somewhere.

NOTE: can only be called on a wrapper instance that is also the root instance.


#### Returns

`ShallowWrapper`: Returns itself.



#### Example

```jsx
class ImpureRender extends React.Component {
constructor(props) {
super(props);
this.count = 0;
}
render() {
return <div>{this.count++}</div>
}
}
```
```jsx
const wrapper = shallow(<ImpureRender />);
expect(wrapper.text()).to.equal("0");
wrapper.update();
expect(wrapper.text()).to.equal("1");
```
3 changes: 3 additions & 0 deletions docs/api/shallow.md
Expand Up @@ -169,6 +169,9 @@ Manually sets context of the root component.
#### [`.instance() => ReactComponent`](ShallowWrapper/instance.md)
Returns the instance of the root component.

#### [`.update() => ShallowWrapper`](ShallowWrapper/update.md)
Calls `.forceUpdate()` on the root component instance.

#### [`.debug() => String`](ShallowWrapper/debug.md)
Returns a string representation of the current shallow render tree for debugging purposes.

Expand Down
15 changes: 14 additions & 1 deletion src/ShallowWrapper.js
Expand Up @@ -145,10 +145,23 @@ class ShallowWrapper {
}

/**
* @deprecated
* Forces a re-render. Useful to run before checking the render output if something external
* may be updating the state of the component somewhere.
*
* NOTE: can only be called on a wrapper instance that is also the root instance.
*
* @returns {ShallowWrapper}
*/
update() {
if (this.root !== this) {
throw new Error('ShallowWrapper::update() can only be called on the root');
}
this.single(() => {
const instance = this.instance();
if (instance) {
instance.forceUpdate();
}
});
return this;
}

Expand Down
26 changes: 23 additions & 3 deletions test/ShallowWrapper-spec.jsx
Expand Up @@ -3726,24 +3726,37 @@ describe('shallow', () => {
}

class Test extends React.Component {
componentWillMount() {
this.state = {};
}

safeSetState(newState) {
withSetStateAllowed(() => {
this.setState(newState);
});
}

asyncUpdate() {
asyncSetState() {
setImmediate(() => {
this.safeSetState({ showSpan: true });
});
}

mutateState() {
this.state.showSpan = true;
}

callbackSetState() {
this.safeSetState({ showSpan: true });
}

render() {
return (
<div>
{this.state && this.state.showSpan && <span className="show-me" />}
<button className="async-btn" onClick={() => this.asyncUpdate()} />
<Child callback={() => this.safeSetState({ showSpan: true })} />
<button className="async-btn" onClick={() => this.asyncSetState()} />
<button className="mutates-btn" onClick={() => this.mutateState()} />
<Child callback={() => this.callbackSetState()} />
</div>
);
}
Expand All @@ -3763,6 +3776,13 @@ describe('shallow', () => {
wrapper.find(Child).props().callback();
expect(wrapper.find('.show-me').length).to.equal(1);
});

it('should have updated output after state mutation when .update() is called', () => {
const wrapper = shallow(<Test />);
wrapper.find('.mutates-btn').simulate('click');
wrapper.update();
expect(wrapper.find('.show-me').length).to.equal(1);
});
});

describe('#single()', () => {
Expand Down

0 comments on commit 74f62b6

Please sign in to comment.