I've just set up a little test project using react native. It's all new to me (including ECMAScript 6). Eslint is telling me something about "circular dependencies" and i don't know how to solve this problem. The code is working nevertheless.
my package.json:
...
"dependencies": {
"axios": "^0.19.0",
"node-sass": "^4.12.0",
"react": "16.8.3",
"react-native": "0.59.9",
"react-navigation": "^3.11.0"
},
"devDependencies": {
"@babel/core": "7.4.5",
"@babel/runtime": "7.4.5",
"babel-eslint": "^10.0.1",
"babel-jest": "24.8.0",
"babel-plugin-module-resolver": "^3.2.0",
"eslint": "^5.16.0",
"eslint-config-airbnb": "^17.1.0",
"eslint-import-resolver-babel-module": "^5.1.0",
"eslint-plugin-import": "^2.17.3",
"eslint-plugin-jsx-a11y": "^6.2.1",
"eslint-plugin-react": "^7.13.0",
"jest": "24.8.0",
"metro-react-native-babel-preset": "0.54.1",
"react-dom": "^16.8.6",
"react-test-renderer": "16.8.3"
},
...
src/index.jsx
is the main JSX file:
import { Comp1 } from 'components';
...
I created a src/components/index.jsx
to enable imports like
import { Comp1, Comp2, Comp3 } from 'components'
instead of
import { Comp1 } from 'components/comp1';
import { Comp2 } from 'components/comp2';
import { Comp3 } from 'components/comp3';
The file src/components/index.jsx
looks like:
export * from './button';
export * from './comp1';
...
src/components/button/index.jsx
:
import React from 'react';
import {
Text,
TouchableOpacity
} from 'react-native';
import style from './style';
const Button = ({ onPress, children }) => {
const {
buttonStyle,
textStyle
} = style;
return (
<TouchableOpacity onPress={onPress} style={buttonStyle}>
<Text style={textStyle}>
{children}
</Text>
</TouchableOpacity>
);
};
export default Button;
export { Button };
src/components/comp1/index.jsx
:
import React from 'react';
import {
Text,
View
} from 'react-native';
import { Button } from 'components';
import style from './style';
const Comp1 = (props) => {
const {
textStyle,
viewStyle
} = style;
return (
<View style={viewStyle}>
<Text style={textStyle}>some text</Text>
<Button>Test</Button>
</View>
);
};
export default Comp1;
export { Comp1 };
Running this setup produces an eslint error import/no-cycle. The code itself works.
If i change import { Button } from 'components'
in src/components/comp1/index.jsx
into import { Button } from 'components/button'
the no eslint error pops up.
I would like to use this short import syntax like described above without loosing the possibility of using the modules inside of each other. Is there a way?
Your structure is setting up a cyclic dependency between components/index.jsx
and comp1/index.jsx
(and others where you have this same thing). comp1/index.jsx
imports from components/index.jsx
, which imports from comp1/index.jsx
.
The runtime handling of cycles is different between actual native ESM¹ modules and CJS² or similar. Both require careful handling of cycles, but the ways they behave differ slightly. This can be particularly confusing if you're transpiling ESM to CJS in your bundler or similar.
When there's a cyclic dependency between two modules (to keep it simple), it means at some point one of the two modules will run before the other, which means any imports it takes from the other will either be uninitialized (ESM) or not yet be defined (CJS). So top-level code in one of the two modules can't rely on the imports existing yet. In ESM, trying to use an uninitialized export raises an error; in CJS, the export's value is just undefined
.
In your example, I wouldn't expect that to be a problem, because the top-level code in the modules in the cycle doesn't use the imports from the other module in the cycle, it's only used in functions called later (comp1/index.jsx
uses Button
, but only when Comp1
is called, and it's not called at the top-level code. (The process can be more complicated than that, but...)
If the code is tested and working, you might use configuration comments to disable that rule for the component files (if it lets you do that). This lets you leave the rule on globally in case of actually-problematic cycles, but not get bothered about these cycles you've tested and know are good.
¹ ESM = ECMAScript Modules, genuine native JavaScript modules, which are statically analyzable if you use only import
and export
declarations (not import()
dynamic imports).
² CJS = CommonJS, a dynmic module structure using require
and an exports
object.
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