I have a relatively simple React component that renders a list based on its state. Then I have a karma/jasmine test that renders the component, sets its state, and checks that the correct markup is rendered.
The problem I'm running into is that every time I do a setState({})
or forceUpdate()
on my component, I'm getting an error:
TypeError: 'undefined' is not an object (evaluating 'deepestAncestor.firstChild')
at /home/company/projects/user_interface_kit/bower_components/react/react.js:10314
What's the correct way to test state changes in a React component?
var NotificationCenter = React.createClass({
getInitialState: function(){
return {notifications:[]}
},
render: function() {
countContainerStyle = {
display: this.state.notifications.length > 0 ? '' : 'none'
};
return (
<div id="pc-notification-center">
<span className="pc-notification-center-bell" >
B
</span>
<span className="pc-notification-count-container" style={countContainerStyle}>
<span className="pc-notification-count-circle">●</span>
<span className="pc-notification-count">{this.state.notifications.length}</span>
</span>
</div>);
}
});
return NotificationCenter;
});
it('should set its notification count to the number of notifications it has', function() {
var notificationCenter = NotificationCenter({}),
countNode;
TestUtils.renderIntoDocument(notificationCenter);
notificationCenter.setState({
notifications: [1,2]
});
countNode = TestUtils.findRenderedDOMComponentWithClass(notificationCenter,'pc-notification-count');
expect(countNode).toBe(2);
});
PhantomJS 1.9.7 (Linux) [object Object] [object Object] [object Object] should default its notification count to 0 FAILED
TypeError: 'undefined' is not an object (evaluating 'deepestAncestor.firstChild')
at /home/company/projects/user_interface_kit/bower_components/react/react.js:10314
at /home/company/projects/user_interface_kit/bower_components/react/react.js:10260
at getNode (/home/company/projects/user_interface_kit/bower_components/react/react.js:9874)
at /home/company/projects/user_interface_kit/bower_components/react/react.js:4472
at /home/company/projects/user_interface_kit/.tmp/notification_center/popover.js:28
at /home/company/projects/user_interface_kit/bower_components/react/react.js:5925
at /home/company/projects/user_interface_kit/.tmp/notification_center/popover.js:75
at /home/company/projects/user_interface_kit/bower_components/react/react.js:5925
at /home/company/projects/user_interface_kit/.tmp/notification_center/popover.js:81
at /home/company/projects/user_interface_kit/bower_components/react/react.js:10461
at /home/company/projects/user_interface_kit/bower_components/react/react.js:11924
at /home/company/projects/user_interface_kit/bower_components/react/react.js:13944
at /home/company/projects/user_interface_kit/bower_components/react/react.js:13877
at /home/company/projects/user_interface_kit/bower_components/react/react.js:4360
at /home/company/projects/user_interface_kit/bower_components/react/react.js:10055
at /home/company/projects/user_interface_kit/bower_components/react/react.js:11169
at /home/company/projects/user_interface_kit/bower_components/react/react.js:10105
at /home/company/projects/user_interface_kit/bower_components/react/react.js:11169
at /home/company/projects/user_interface_kit/.tmp/notification_center/notification_center.js:50
at /home/company/projects/user_interface_kit/bower_components/react/react.js:10461
at /home/company/projects/user_interface_kit/bower_components/react/react.js:11924
at /home/company/projects/user_interface_kit/bower_components/react/react.js:13944
at /home/company/projects/user_interface_kit/bower_components/react/react.js:13877
at /home/company/projects/user_interface_kit/bower_components/react/react.js:4360
at /home/company/projects/user_interface_kit/bower_components/react/react-with-addons.js:10483
at /home/company/projects/user_interface_kit/bower_components/react/react-with-addons.js:11597
at /home/company/projects/user_interface_kit/bower_components/react/react-with-addons.js:10533
at /home/company/projects/user_interface_kit/bower_components/react/react-with-addons.js:11597
at /home/company/projects/user_interface_kit/bower_components/react/react-with-addons.js:12716
at /home/company/projects/user_interface_kit/test/notification_center/notification_center_test.js:19
at /home/company/projects/user_interface_kit/node_modules/karma-jasmine/lib/adapter.js:171
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1585
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:841
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1074
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:126
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1117
at each (/home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:58)
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1118
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:895
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1074
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:126
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1117
at each (/home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:58)
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1118
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:895
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1074
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:126
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1117
at each (/home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:58)
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1118
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:895
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1074
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:126
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1117
at each (/home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:58)
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1118
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:895
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1074
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:126
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1117
at each (/home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:58)
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1118
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:895
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1074
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:126
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1117
at each (/home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:58)
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1118
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:895
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1074
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:126
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1117
at each (/home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:58)
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1118
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:895
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1074
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:126
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1117
at each (/home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:58)
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1118
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:895
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1104
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:754
at callGetModule (/home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1129)
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1479
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1606
Here is a small project reproducing the issue https://github.com/treehau5/react_karma_requirejs_bug_reproduction
To update our state, we use this. setState() and pass in an object. This object will get merged with the current state. When the state has been updated, our component re-renders automatically.
State updates in React are asynchronous; when an update is requested, there is no guarantee that the updates will be made immediately. The updater functions enqueue changes to the component state, but React may delay the changes, updating several components in a single pass.
You can call setState() immediately in componentDidMount() and triggers an extra rendering, but this happens before the browser updates the screen, calling render() twice.
We have to set initial state value inside constructor function and set click event handler of the element upon which click, results in changing state. Then pass the function to the click handler and change the state of the component inside the function using setState.
After digging deep into how React and React_with_addons works, it looks like the local React object in the test is different than the instance of React that React_with_addons uses. So if you do this in your test:
react_with_addons.addons.TestUtils.renderIntoDocument(componentInstance);
Then react_with_addon's instance of React registers the componentInstance and primes its nodeCache object. The local React instance remains is untouched. Then, if you try to update the component's state like this:
componentInstance.updateState({key:'newValue'});
The local instance of React is used to try to update the DOM. Because no components have ever been mounted with this instance, the update fails and you get the 'undefined' is not an object (evaluating 'deepestAncestor.firstChild') error.
Interestingly, if you mount ANY component into the local React, then you won't see an error when updating a component's state, even if that component was mounted using react_with_addon's React object.
The best way to avoid this problem for now, if your test needs to run setState, is to simply not use react_with_addon's renderIntoDocument function. Instead, just create your own. Its only two lines:
function renderIntoDocument(instance) {
var div = document.createElement('div');
return React.renderComponent(instance, div);
};
I don't know if the previous answer is still true.
With React 0.11.1, this code works fine:
var React = require('react/addons');
var TestUtils = React.addons.TestUtils;
jest.dontMock('public/components/MyThing.jsx');
var MyThing = require('public/components/MyThing.jsx');
describe('MyThing', function() {
var html;
describe('#render', function() {
beforeEach(function(){
var component = MyThing();
var componentInstance = TestUtils.renderIntoDocument(component);
componentInstance.setState({isCool: true});
html = componentInstance.getDOMNode().textContent;
});
it('includes something cool', function(){
expect(html).toContain('something cool');
});
});
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With