Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to fix 'Invariant Violation' caused by cyclic dependencies in react-redux

I have a React project set up like this:

enter image description here

It is a simple application. The Dashboard has a UserListContainer, containing a UserList, which lists four users with their ID and name. The UserList gets the Users from Data.ts

The application itself works just fine and displays the four users. But as soon as I try to test the UserList with enzymes shallow rendering, the tests give me the following error message:

Invariant Violation: You must pass a component to the function returned by connect. Instead received undefined
      at invariant (node_modules/invariant/invariant.js:40:15)
      at wrapWithConnect (node_modules/react-redux/lib/components/connectAdvanced.js:97:33)
      at Object.<anonymous> (src/Users/UserListContainer.tsx:4:34)
      at Object.<anonymous> (src/Users/index.ts:1:1)
      at Object.<anonymous> (src/Dashboard/Dashboard.tsx:2:1)
      at Object.<anonymous> (src/Dashboard/index.ts:1:1)
      at Object.<anonymous> (src/Users/UserList.tsx:2:1)
      at Object.<anonymous> (src/Users/__tests__/UserList.test.tsx:3:1)

The problem is basically that, even though we don't use the Dashboard when rendering the UserList shallowly, React still tries to build it. I guess that happens because we access Data through the Dashboard index, so React will also try to resolve Dashboard and its imports, namely UserListContainer, because they are exported through the same index file. When I import the users directly instead of through the index, the problem disappears.

We fixed this issue by breaking the cyclic dependency but if I encounter the error again, I want to know other ways to fix it. I would also like to understand why the web application still seems to be working just fine, while the tests fail.

Also, is there a way to prevent React from resolving the imports and exports when using enzymes shallow rendering?

Users/__tests__/UserList.test.tsx

test("reproduce the problem", () => {
  const wrapper = shallow(<UserList />)
  console.log(wrapper)
  expect(1).toBe(1)
})

Users/UserList.tsx

import { Data } from "../Dashboard"
export const UserList: React.FC = () => (
  <React.Fragment>
    {Data.users.map(user => (
      <div>
        <code>{user.id} - </code>
        <code>{user.name}</code>
      </div>
    ))}
  </React.Fragment>
)

Dashboard/index.ts

export { Dashboard } from "./Dashboard" // not used but still resolved
export { Data } from "./Data" // actually used

Dashboard/Data.ts

export const Data = {
  users: [
    { id: "user1", name: "Albert" },
    { id: "user2", name: "Bertha" },
    { id: "user3", name: "Chloe" },
    { id: "user4", name: "Doug" }
  ]
}

Dashboard/Dashboard.tsx

import { UserListContainer } from "../Users"
export const Dashboard: React.FC = () => {
  return <UserListContainer />
}

Users/UserListContainer.tsx

import { UserList } from "./UserList"
export const UserListContainer = connect()(UserList)
like image 630
Tommus Avatar asked Nov 11 '19 15:11

Tommus


1 Answers

One way of fixing it would be to reorder the imports in your dashboard file:

export { Data } from "./Data" // actually used 
export { Dashboard } from "./Dashboard" // not used but still resolved

Web application will work in most cases because it will start resolving from your index.tsx file (or whatever your entry file name is) and go from there. Jest on the other hand starts from your test file and only resolves those imports (you can find a nice explanation why this happens here: https://railsware.com/blog/how-to-analyze-circular-dependencies-in-es6/).

We had similar problems in our projects and unfortunatelly except reordering imports and better structuring your files there is no other solution.

One "hack" that you can also do is to add:

import 'problematic_module'

to your jest setupFilesAfterEnv. That way module will be resolved before each test (but i recommend this only as a last resort).

like image 108
Kaca992 Avatar answered Oct 23 '22 11:10

Kaca992