javascript - Hvordan fikse 'Invariant Violation' forårsaket av sykliske avhengigheter i react-redux
Jeg har et React-prosjekt satt opp på denne måten:
Det er en enkel applikasjon. Dashboard har en UserListContainer, som inneholder en UserList, som viser fire brukere med deres ID og navn. UserList henter brukerne fra Data.ts
Selve applikasjonen fungerer helt fint og viser de fire brukerne. Men så snart jeg prøver å teste UserList med enzymers grunne rendering, får testene følgende feilmelding:
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)
Problemet er i utgangspunktet at, selv om vi ikke bruker Dashboard når vi renderer UserList grunnleggende, prøver React fortsatt å bygge det. Jeg antar at det skjer fordi vi får tilgang til Data gjennom Dashboard-indeksen, så React vil også prøve å løse Dashboard og dets imports, nemlig UserListContainer, fordi de eksporteres gjennom samme indeksfil. Når jeg importerer brukerne direkte i stedet for gjennom indeksen, forsvinner problemet.
Vi løste dette problemet ved å bryte den sykliske avhengigheten, men hvis jeg møter feilen igjen, vil jeg vite andre måter å fikse det på. Jeg vil også gjerne forstå hvorfor webapplikasjonen fortsatt ser ut til å fungere helt fint, mens testene feiler.
Er det også en måte å forhindre at React løser importene og eksportene når man bruker enzymes shallow rendering?
Brukere/__tester__/UserList.test.tsx
test("reproduce the problem", () => {
const wrapper = shallow(<UserList />)
console.log(wrapper)
expect(1).toBe(1)
})
Brukere/Brukerliste.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>
)
Dashbord/index.ts
export { Dashboard } from "./Dashboard" // not used but still resolved
export { Data } from "./Data" // actually used
Dashbord/Data.ts
export const Data = {
users: [
{ id: "user1", name: "Albert" },
{ id: "user2", name: "Bertha" },
{ id: "user3", name: "Chloe" },
{ id: "user4", name: "Doug" }
]
}
Dashbord/Dashbord.tsx
import { UserListContainer } from "../Users"
export const Dashboard: React.FC = () => {
return <UserListContainer />
}
Brukere/BrukerlisteContainer.tsx
import { UserList } from "./UserList"
export const UserListContainer = connect()(UserList)
Svar #1
En måte å fikse det på ville være å omorganisere importene i dashbord-filen din:
export { Data } from "./Data" // actually used
export { Dashboard } from "./Dashboard" // not used but still resolved
Webapplikasjonen vil fungere i de fleste tilfeller fordi den vil begynne å løse fra din index.tsx-fil (eller hva enn navnet på inngangsfilen din er) og gå derfra. Jest, derimot, starter fra testfilen din og løser kun de importene (du kan finne en fin forklaring på hvorfor dette skjer her: https://railsware.com/blog/how-to-analyze-circular-dependencies-in-es6/).
Vi hadde lignende problemer i våre prosjekter, og dessverre er det ingen annen løsning enn å omorganisere importene og strukturere filene dine bedre.
En "løsning" som du også kan gjøre er å legge til:
import 'problematic_module'
til din jest setupFilesAfterEnv. På den måten vil modulen bli løst før hver test (men jeg anbefaler dette bare som en siste utvei).
