Redux testing - Invariant violation error
I came across this error "Invariant Violation: You must pass a component to the function returned by connect. Instead received undefined" when trying to debug React/Redux app. This article walks through how I went about to solve it, and what was the issue.
I came across this weird error when trying to test some React components.
Test suite failed to run
Invariant Violation: You must pass a component to the function returned by connect. Instead received undefined
The structure is very simple, there is a Redux container wrapping a React presentational component with local state.
// Container component
const mapStateToProps = ...
const mapDispatchToProps = ...
export default connect (
mapStateToProps,
mapDispatchToProps
)(Container);
// Component
export default class Component extends React.Component {
constructor(props) {
super(props);
this.state = {
foo: null
};
...
}
render() {
return (
<div>Component</div>
);
}
}
I wrote tests for both of the files above, the container tests ran fine. However, when I try to run my test in Jest I'd get this error.
Test suite failed to run
Invariant Violation: You must pass a component to the function returned by connect. Instead received undefined
I checked my container's connect()
function, but everything looks fine. After doing a little research, other people mentioned their issues were caused by one of the following issues:
- Circular dependencies between components
- Wrong usage of
export
andexport default
then imported the wrong way - Used the connect function wrongly, passed the wrong parameters
I checked against all these points, but none of it applies to the code I was working with.
What's even more strange is that I could build and run the web app no problem. So this problem seems to only exist in Jest.
So I decided to do more debugging around it, "when stuck, trial and error" right? I commented out the line which does the rendering, which looked something like this:
it(('should not fail') => {
// const wrapper = shallow(<Component />);
expect(true).toBe(false);
});
This test obviously failed, and it actually ran the test suite. Which is a good news, because I was never getting this far before. The fact the rendering is failing must mean my export was done right, or something related to it. So I went back and checked again, and same as before I found nothing.
I tried commenting back all the shallow render and commented the import at the top of the file (see below). This also ran the test suite giving me the error that Component is undefined.
// import Component from './Component';
it(('should not fail') => {
const wrapper = shallow(<Component />);
});
The issue
It turns out the problem was still circular dependency, but the shared dependency was something really stupid. Let me explain.
I am using Typescript, I declared a type a few levels higher and then imported it in Component. This made that entire high level component dependent on the Component, thus causing the circular dependency.
As suspected it was a simple fix, but I had to go through so many loops. For anyone getting this error, check the 3 points mentioned above because I think they will cover 99.99% of the cases.
Also note to self:
- Circular dependency doesn't get picked up by Typescript in React/Redux project. This could be a limitation of webpack or my webpack config, as when I used Typescript with rollup.js it did pick up circular dependency.
- Typescript will build React app with Circular dependency, but it won't run the test suite
The real life example
Here is the exact error I got so people can compare to see if they have the same problem. SpendPointsMenu
in this case is the Component in my example above, and SpendPointsMenuContainer
is obviously the container. Spend
is the high level component where I was importing the type from. Don't worry too much about SpendPoints
it just sits in between Spend
and SpendPointsMenuContainer
.
Test suite failed to run
Invariant Violation: You must pass a component to the function returned by connect. Instead received undefined
26 | )(SpendPointsMenu);
27 |
at invariant (node_modules/invariant/invariant.js:40:15)
at wrapWithConnect (node_modules/react-redux/lib/components/connectAdvanced.js:101:29)
at Object.<anonymous> (src/views/spend/spendPoints/SpendPointsMenuContainer.tsx:28:141)
at Object.<anonymous> (src/views/spend/spendPoints/SpendPoints.tsx:13:33)
at Object.<anonymous> (src/views/spend/Spend.tsx:16:46)
at Object.<anonymous> (src/views/spend/spendPoints/SpendPointsMenu.tsx:20:40)
at Object.<anonymous> (src/views/spend/spendPoints/SpendPointsMenu.test.tsx:7:461)
My first clue should have been:
SpendPointsMenu.tsx is causing an error in SpendPointsMenu.test.tsx, that's fine because that's the file I trying to test. But why is Spend.tsx causing an error in SpendPointsMenu.tsx.
I over looked this clue, and ended up spending hours trying to fix something very simple.