React Cyclic dependency importing named export - reactjs

I have 2 containers, both import a component called PageHeader this is a functional component and is imported / exported as like so....
( this happens with any component I import, not just PageHeader )
components/PageHeader/PageHeader.jsx
import React from 'react';
import PropTypes from 'prop-types';
import './PageHeader.sass';
const PageHeader = ({ title, description, image }) => (
<div className="section is-paddingless is-hidden-touch">
<div className="header header__desktop" style={{ backgroundImage: `url('${image}')` }}>
<h3 className="is-size-3 has-text-weight-semibold">{title}</h3>
<h5>{description}</h5>
</div>
</div>
);
PageHeader.propTypes = {
title: PropTypes.string.isRequired,
description: PropTypes.string.isRequired,
image: PropTypes.string.isRequired,
};
export default PageHeader;
components/PageHeader/index.js
import PageHeader from './PageHeader';
export default PageHeader;
In the root of my components folder I then have....
components/index.js
......
export { default as PageHeader } from './PageHeader';
......
This allows me to import components into a container like so....
import { DelayedComponent, Loading, Page, PageHeader, ClientList, RecentlyViewed, ClientFilter } from '../../components';
When I only had 1 container, this was working fine, I have now introduced a second container, this is essentially a second page.
My Containers are as follows....
containers/Clients/Clients.jsx
import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import { DelayedComponent, Loading, Page, PageHeader, ClientList, RecentlyViewed, ClientFilter } from '../../components';
import headerImage from '../../assets/images/client-search-banner.jpg';
class Clients extends Component {
state = { selector: 'ALL' };
static propTypes = {
fetchClientListAction: PropTypes.func.isRequired,
selectClientAction: PropTypes.func.isRequired,
isLoading: PropTypes.bool.isRequired,
clients: PropTypes.array.isRequired,
};
componentDidMount() {
this.props.fetchClientListAction();
}
onClientSelect = client => this.props.selectClientAction(client);
onFilterTenantList = selector => this.setState({ selector });
render() {
const { isLoading, clients } = this.props;
const { selector } = this.state;
if (isLoading)
return (
<DelayedComponent>
<Loading />
</DelayedComponent>
);
return (
<Fragment>
<PageHeader
title="Choose a client"
description="Bacon ipsum dolor amet ribeye biltong tongue, pig brisket venison fatback pork bacon kielbasa burgdoggen salami strip steak."
image={headerImage}
/>
<Page>
<Page.Columns>
<Page.Column modifiers="is-paddingless">
<ClientFilter clients={clients} selector={selector} filterByLetter={this.onFilterTenantList} />
</Page.Column>
</Page.Columns>
<Page.Columns modifiers="is-multiline">
<Page.Column modifiers="is-two-thirds">
<ClientList clients={clients} selector={selector} onClientSelect={this.onClientSelect} />
</Page.Column>
<Page.Column modifiers="is-one-third">
<RecentlyViewed />
</Page.Column>
</Page.Columns>
</Page>
</Fragment>
);
}
}
export default Clients;
containers/EmployeeSearch/EmployeeSearch.jsx
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { PageHeader } from '../../components';
import headerImage from '../../assets/images/client-search-banner.jpg';
class EmployeeSearch extends Component {
render() {
return (
<PageHeader
title="Search"
description="Bacon ipsum dolor amet ribeye biltong tongue, pig brisket venison fatback pork bacon kielbasa burgdoggen salami strip steak."
image={headerImage}
/>
);
}
}
export default EmployeeSearch;
Doing this causes Webpack crash with the following error...
/Users/xxxxx/Code/xxxxx/search-webapp/node_modules/toposort/index.js:29
throw new Error('Cyclic dependency: '+JSON.stringify(node))
^
TypeError: Converting circular structure to JSON
at JSON.stringify (<anonymous>)
at visit (/Users/xxxxx/Code/xxxxx/search-webapp/node_modules/toposort/index.js:29:50)
at visit (/Users/xxxxx/Code/xxxxx/search-webapp/node_modules/toposort/index.js:47:9)
at visit (/Users/xxxxx/Code/xxxxx/search-webapp/node_modules/toposort/index.js:47:9)
at Function.toposort [as array] (/Users/xxxxx/Code/xxxxx/search-webapp/node_modules/toposort/index.js:22:22)
at Object.module.exports.dependency (/Users/xxxxx/Code/xxxxx/search-webapp/node_modules/html-webpack-plugin/lib/chunksorter.js:50:35)
at HtmlWebpackPlugin.sortChunks (/Users/xxxxx/Code/xxxxx/search-webapp/node_modules/html-webpack-plugin/index.js:364:35)
at /Users/xxxxx/Code/xxxxx/search-webapp/node_modules/html-webpack-plugin/index.js:113:21
at AsyncSeriesHook.eval [as callAsync] (eval at create (/Users/xxxxx/Code/xxxxx/search-webapp/node_modules/tapable/lib/HookCodeFactory.js:24:12), <anonymous>:7:1)
at AsyncSeriesHook.lazyCompileHook [as _callAsync] (/Users/xxxxx/Code/xxxxx/search-webapp/node_modules/tapable/lib/Hook.js:35:21)
error An unexpected error occurred: "Command failed.
However, if I change the import in EmployeeSearch.jsx so it looks like this....
import PageHeader from '../../components/PageHeader';
It works.
I am at a complete loss and would love some input as to why / how this happening and how I can fix this without adding multiple import statements for components.
My Containers are rendered as follows....
import React from 'react';
import Loadable from 'react-loadable';
import { Switch, Route } from 'react-router-dom';
import Loading from './components/Loading';
const AsyncRoute = loader =>
Loadable({
loader,
loading: Loading,
delay: 300,
});
const Clients = AsyncRoute(() => import(/* webpackChunkName: "clients" */ './containers/Clients'));
const EmployeeSearch = AsyncRoute(() => import(/* webpackChunkName: "employee-search" */ './containers/EmployeeSearch'));
const Routes = () => (
<Switch>
<Route exact path="/" component={Clients} />
<Route path="/employee-search/:client" component={EmployeeSearch} />
</Switch>
);
export default Routes;

It turns out I had included App within my components index.js file.
My routes are wrapped in App, so that was my circular dependancy.

Related

SyntaxError: Cannot use import statement outside a module with dynamic import of Nextjs

I followed the doc of SunEditor, it's like:
import React from 'react';
import dynamic from "next/dynamic";
import 'suneditor/dist/css/suneditor.min.css'; // Import Sun Editor's CSS File
const SunEditor = dynamic(() => import("suneditor-react"), {
ssr: false,
});
const MyComponent = props => {
return (
<div>
<p> My Other Contents </p>
<SunEditor />
</div>
);
};
export default MyComponent;
It works well, but when I add setOptions into SunEditor:
import { buttonList } from "suneditor-react";
...
<SunEditor
setOptions={{buttonList:buttonList.complex}}
/>
I got this error:
SyntaxError: Cannot use import statement outside a module
Am I missing something, and how can I fix it?
For the same reason you have to dynamically import SunEditor, you also have to dynamically import buttonList.
One approach is to create a custom component where you add all the suneditor code.
import React from 'react';
import SunEditor, { buttonList } from 'suneditor-react';
const CustomSunEditor = () => {
return <SunEditor setOptions={{ buttonList: buttonList.complex }} />;
};
export default CustomSunEditor;
Then, dynamically import that component with next/dynamic where needed.
const CustomSunEditor = dynamic(() => import('../components/CustomSunEditor'), {
ssr: false,
});
const MyComponent = props => {
return (
<div>
<p> My Other Contents </p>
<CustomSunEditor />
</div>
);
};

Passing parent css source to child component in gatsby

I have 4 sections in my homepage src/pages/index.js in order to make it tidy I want to break the section into 4 different components, let say src/components/foo.js src/components/bar.js src/components/baz.js and src/components/qux.js
src/pages/index.js
import React from 'react';
import {graphql} from 'gatsby';
import homeStyle from '../styles/homepage.module.scss'; // how to pass this style into Foo
import Foo from '../components/foo'
const HomePage = ({data: {ipsum}}) => {
return(
<div>
<Foo data={ipsum} />
</div>
)
}
export const loremQuery = graphql`
query {
ipsum: homepageYaml {
id
heading
}
}
`
export default HomePage
my question is how to pass/get homeStyle (css module) above into my component below, so i dont have to reimport the style on each component inside index.js
src/components/foo.js
import React from 'react';
const Hero = ({data}) => {
return(
<h1 className={?}>{data.heading}</h1>
)
}
export default Hero

Jest: mock one of several exported components

In component I want to test I import several components from a parent component:
import {
ResponsiveContainer,
LineChart,
Line,
XAxis,
YAxis,
CartesianGrid,
Legend,
} from 'recharts';
How do I mock just ResponsiveContainer leaving other components untouched?
Update: Dennie de Lange prompted to use dependency injection in this case. Completely agree. But I faced the following problem. Here is my component:
import React, { Component } from 'react';
import {
ResponsiveContainer,
LineChart,
} from 'recharts';
import moment from 'moment';
class CustomLineChart extends Component<Props> {
render() {
const { data, container: Container = ResponsiveContainer } = this.props;
if (!data) return null;
return (
<div className="lineChart">
<Container>
<LineChart data={data}>
... lots of other stuff here
</LineChart>
</Container>
</div>
);
}
}
export default CustomLineChart;
and here's my test:
const Mock = ({ children }) => <div>{children}</div>;
describe('<LineChart />', () => {
it('renders data if provided', () => {
const component = create(<LineChart data={data} container={Mock} />);
const tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
});
And here's what snapshot I get for this:
<div
className="lineChart"
>
<div />
</div>
So children of Container are missing for some reason. Any ideas?
Update 2: Actually the above implementation is correct, this was LineChart rendering nothing in my case.
By making it configurable in the component you want to test?
const MyTestComponent = ({container:Container=ResponsiveContainer}) => ...
This allows you to create a mock and supply it to the MyTestComponent

Uncaught TypeError: Cannot read property 'CSSTransitionGroup' of undefined, react addons

I need to use react addons, ReactCSSTransitionGroup, but still I have error Uncaught TypeError: Cannot read property 'CSSTransitionGroup' of undefined.
I have react, react-dom and ReactCSSTransitionGroup v: 15.4.2, so shouldn't have problem with it. I have installed react-addons-css-transition-group via mpn.
import React, { PropTypes } from 'react';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
const ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
I add to webpack configuration
externals: {
'react/addons': true,
'react/lib/ExecutionEnvironment': true,
'react/lib/ReactContext': true
}
but it didn't help
I use it in index.js (in Alert folder):
import React, { PropTypes } from 'react';
import BasicAlert from './basicAlert';
import {
alertsWrapper,
enter,
enterActive,
leave,
leaveActive,
} from './alertsHandler.scss';
import CSSTransitionGroup from 'react-addons-css-transition-group';
//const ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
const AlertsHandler = ({ closeTime, alerts = [] }) => (
<div className={alertsWrapper}>
<ReactCSSTransitionGroup
transitionName={{
enter,
enterActive,
leave,
leaveActive,
}}
transitionEnterTimeout={500}
transitionLeaveTimeout={500}
>
{alerts.map(item => (
<BasicAlert
closeTime={closeTime}
{...item}
key={`alert${item.id}`}
/>
))}
</ReactCSSTransitionGroup>
</div>
);
AlertsHandler.propTypes = {
closeTime: PropTypes.number,
alerts: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.string,
type: PropTypes.string,
alertContent: PropTypes.node,
repeated: PropTypes.number,
})),
};
export default AlertsHandler;
and import in App.js:
import React from 'react';
import Alert from '../components/Alerts';
var example = 'whatever';
export default class App extends React.Component {
render() {
return (
<div>
<Alert closeTime={3000} alerts={example} />
</div>);
}
}
I try to import: import React, { PropTypes } from 'react/addons'; and import { __Addons as addons, PropTypes } from 'react' but it give me error canno't read property addons of undefine. I even tryed to import directly from node_modules or use depreciate module react-addons.
I'm not sure, but I think there is problem with import react-with-addons, but I can't find properly way to do it.
If I gave to less information, please ask.
In my case, I was using a className prop on my CSSTransition component. It should be classNames. Spent hours figuring that out... just posting in case it helps somebody.
ReactCSSTransitionGroup is not a export from the main React package.
Just remove the third line const ReactCSSTransitionGroup = ....
import React, { PropTypes } from 'react';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
This should work
Update 1:
Subsequent error being thrown can be resolved like so
In index.js (in Alert folder):
import React, { PropTypes } from 'react';
...
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
//const ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
const AlertsHandler = ({ closeTime, alerts = [] }) => (
<div className={alertsWrapper}>
<ReactCSSTransitionGroup
...
</ReactCSSTransitionGroup>
</div>
);
...

Changing Application State based on Router

I'm creating a application where I have a component which shows data based on the router location
for example
localhost:8080/us should show "USA flag and some text related to United States"
localhost:8080/in should show "India flag and some text related to India"
I have two reducers one for India and other for US and I have a Root reducer as shown bellow
import {combineReducers} from 'redux';
import IndiaData from './IndiaData';
import USData from './UsData';
const rootReducer = combineReducers({
IndiaData,
USData,
ActiveData
});
export default rootReducer;
My Sample reducer IndiaData is as bellow
export default function() {
return [
{
"StateName": "AP",
"StateImg": "../static/image1.jpg",
},
{
"StateName": "TS",
"StateImg": "../static/image2.jpg",
},
{
"StateName": "TN",
"StateImg": "../static/image3.jpg",
}
]
}
I'm using mapStateToProps in my react component, please find the
code below
import React, {Component} from 'react';
import {connect} from 'react-redux';
import styles from '../styles/custom.css'
class CountryData extends React.Component {
Country(){
return this.props.usdata.map((Data)=>{
return(
<div className="col-md-4">
<img className="panel" src={Data.StateImg}/>
<p>{Data.StateName}</p>
</div>
);
})
}
render(){
return(
<div>
<div className="container margin-top jumbotron">
{this.Country()}
</div>
</div>
);
}
}
function mapStateToProps (state){
return {
indiadata: state.IndiaData,
usdata: state.USData,
};
}
export default connect(mapStateToProps)(CountryData);
and my router configuration is as below
import React from 'react';
import {Route, IndexRoute} from 'react-router';
import App from './components/app';
import CountryData from './components/CountryData';
export default (
<Route path ="/" component={App}>
<Route path="in" component={CountryData} />
<Route path="us" component={CountryData} />
</Route>
);
So Finally I should be able to read what is present in the url path and then show US data or India data dynamically, can some one please help me with this?
Thank you for your support in advance!!
You can make a route like so:
And then access that country param in the container via this.props.params.country.
In your mapStateToProps function, you could use this constant to render the correct country data.
return this.props.data.map((Data)=>{
return(
<div className="col-md-4">
<img className="panel" src={Data.StateImg}/>
<p>{Data.StateName}</p>
</div>
);
})
...
// other code here
...
function mapStateToProps (state, props){
return {
data: props.params.country === 'in'
? state.IndiaData
: state.USData
};
}
Another way would be to export two different connected components.
return this.props.data.map((Data)=>{
return(
<div className="col-md-4">
<img className="panel" src={Data.StateImg}/>
<p>{Data.StateName}</p>
</div>
);
})
...
// other code here
...
function IndiaData (state){
return {
data: state.IndiaData
};
}
function USData (state){
return {
data: state.USData
};
}
export const India = connect(IndiaData)(CountryData);
export const US = connect(USData)(CountryData);
then in your routes:
import React from 'react';
import {Route, IndexRoute} from 'react-router';
import App from './components/app';
import { India, US } from './components/CountryData';
export default (
<Route path ="/" component={App}>
<Route path="in" component={India} />
<Route path="us" component={US} />
</Route>
);

Resources