Basically I'm running a Reactjs application with Highcharts using the highcharts-react-official wrapper. I'm defining a highchart Component and the goal is to generate a report page with a dozen charts with different settings and data sources. I also want to add highstock stock tools components to it. The problem I'm finding is that when I click on Indicators button in a component (first option, top left) popop is triggered in all components.
Chart Component
// (...) Imports
class CustomGUIChart extends Component {
constructor(props) {
super(props);
const { data } = this.props;
this.state = {
options: {
legend: {
enabled: true
},
tooltip: {
enabled: false
},
series: data.map((set, index) => ({
...set,
id: `series-${index}`,
type: "line",
cursor: "pointer",
marker: {
enabled: false
}
})),
stockTools: {
gui: {
enabled: true
}
}
}
};
}
render() {
const { options } = this.state;
return (
<Card>
<CardContent style={{ padding: 0 }}>
<HighchartsReact
highcharts={Highcharts}
constructorType="stockChart"
containerProps={{ className: "chart" }}
options={options}
allowChartUpdate
/>
</CardContent>
</Card>
);
}
}
CustomGUIChart.propTypes = {
data: PropTypes.array.isRequired
};
export default CustomGUIChart;
Page Content calling chart component multiple times
// (...) Imports
const chartData = mockData.map((series) => ({
...series,
data: series.data.map((point) => {
const dateArr = point[0].split('-');
return [
Date.UTC(dateArr[0], dateArr[1], dateArr[2]),
point[1] * 100
];
})
}));
function SectionContent() {
return (
<>
<Grid item xs={12} sm={12}>
<Paper>
<CustomGUIChart
data={chartData}
/>
</Paper>
</Grid>
<Grid item xs={12} sm={12}>
<Paper>
<CustomGUIChart
data={chartData}
/>
</Paper>
</Grid>
<Grid item xs={12} sm={12}>
<Paper>
<CustomGUIChart
data={chartData}
/>
</Paper>
</Grid>
</>
);
}
export default Content;
How to handle this unexpected event trigger in different components/charts?
Here is a working Demo:
You need to declare unique bindingsClassName per each chart.
navigation: {
bindingsClassName: 'chart2'
}
Live demo: https://jsfiddle.net/BlackLabel/y5wxvukr/1/
Related thread: https://github.com/highcharts/highcharts/issues/10599
AP Reference: https://api.highcharts.com/highstock/navigation.bindingsClassName
Related
I'm trying to overwrite the default behaviour of 'noOptionsMessage' (default for noOptionsMessage: noOptionsMessage?: (obj: { inputValue: string }) => string | null;) of react-select using the components prop.
This is what it is stated in the code for react-select:
/*
This complex object includes all the compositional components that are used
in `react-select`. If you wish to overwrite a component, pass in an object
with the appropriate namespace.
*/
components?: SelectComponentsConfig<OptionType>;
This is what I currently have:
const components: Props['components'] = {
LoadingIndicator() {
return null;
},
DropdownIndicator() {
return null;
},
IndicatorSeparator() {
return null;
},
ClearIndicator(props) {
return (
<reactSelectComponents.ClearIndicator {...props}>
<XIcon size="small" />
</reactSelectComponents.ClearIndicator>
);
},
Input(props) {
return (
<reactSelectComponents.Input
{...props}
aria-describedby={(props as any).selectProps['aria-describedby']}
/>
);
},
LoadingMessage() {
return (
<Flex justify="center" marginY="xlarge">
<LoadingDots label="Loading options" />
</Flex>
);
},
ValueContainer(props) {
return (
<reactSelectComponents.ValueContainer {...props}>
<span
css={{
alignItems: 'center',
display: 'flex',
justifyContent: 'center',
width: 40,
}}
>
<SearchIcon color="dim" size="small" />
</span>
{props.children}
</reactSelectComponents.ValueContainer>
);
},
};
export function Autocomplete<Option extends BaseOption>(
props: AutocompleteProps<Option
) {
return (
<Select
components={components}
/>
);
}
I tried moving "const components: Props['components']" inside my Autocomplete component so I have access to the props. But this isn't working as expected. Any thoughts on how I can achieve this??
hi sorry for my bad english.i am using react and redux.i dispatch getTags action in layout component.problem is after getData action called,getDataSuccess action called after my components rendered.so my data is null.
how i can be sure that data is fetched and render my components?
layout:
function DashboardLayout({
children,
showSideBar,
backgroundColor,
getTagsFromServer,
getCategoriesFromServer,
}) {
getTagsFromServer();
getCategoriesFromServer();
return (
<StyledDashbordLayout>
<NavBar />
<SideBarDrawer />
<Grid container className="container">
{showSideBar && (
<Grid item className="sidebar-section">
<SideBar />
</Grid>
)}
<Grid item className="content">
{children}
</Grid>
</Grid>
</StyledDashbordLayout>
);
}
DashboardLayout.propTypes = {
children: PropTypes.node.isRequired,
showSideBar: PropTypes.bool.isRequired,
backgroundColor: PropTypes.string,
getTagsFromServer: PropTypes.func,
getCategoriesFromServer: PropTypes.func,
};
function mapDispatchToProps(dispatch) {
return {
dispatch,
getTagsFromServer: () => dispatch(getTags()),
getCategoriesFromServer: () => dispatch(getCategories()),
};
}
const withConnect = connect(
null,
mapDispatchToProps,
);
export default compose(withConnect)(DashboardLayout);
saga:
import { call, put, takeLatest } from 'redux-saga/effects';
function* uploadVideo({ file }) {
try {
const { data } = yield call(uploadVideoApi, { file });
yield put(uploadFileSuccess(data));
} catch (err) {
yield put(uploadFileFail(err));
}
}
function* getTags() {
const { data } = yield call(getTagsApi);
console.log(data, 'app saga');
yield put(getTagsSuccess(data));
}
function* getCategories() {
const { data } = yield call(getTCategoriesApi);
yield put(getCategoriesSuccess(data));
}
// Individual exports for testing
export default function* appSaga() {
yield takeLatest(UPLOAD_VIDEO, uploadVideo);
yield takeLatest(GET_TAGS, getTags);
yield takeLatest(GET_CATEGORIES, getCategories);
}
this is my select box component which gets null data from store:
import React, { useState } from 'react';
function UploadFileInfo({ tags, categories }) {
return (
<Paper square className={classes.paper}>
<Tabs
onChange={handleChange}
aria-label="disabled tabs example"
classes={{ indicator: classes.indicator, root: classes.root }}
value={tab}
>
<Tab
label="مشخصات ویدیو"
classes={{
selected: classes.selected,
}}
/>
<Tab
label="تنظیمات پیشرفته"
classes={{
selected: classes.selected,
}}
/>
</Tabs>
{tab === 0 && (
<Grid container className={classes.info}>
<Grid item xs={12} sm={6} className={classes.formControl}>
<label htmlFor="title" className={classes.label}>
عنوان ویدیو
</label>
<input
id="title"
type="text"
className={classes.input}
onChange={e => setValue('title', e.target.value)}
defaultValue={data.title}
/>
</Grid>
<Grid item xs={12} sm={6} className={classes.formControl}>
<SelectBox
onChange={e => setValue('category', e.target.value)}
value={data.category}
label="دسته بندی"
options={converItems(categories)}
/>
</Grid>
<Grid item xs={12} className={classes.textAreaWrapper}>
<label htmlFor="info" className={classes.label}>
توضیحات
</label>
<TextField
id="info"
multiline
rows={4}
defaultValue={data.info}
variant="outlined"
classes={{ root: classes.textArea }}
onChange={e => setValue('info', e.target.value)}
/>
</Grid>
<Grid item xs={12} sm={6} className={classes.formControl}>
<SelectBox
onChange={e => {
if (e.target.value.length > 5) {
console.log('hi');
setError(
'tags',
'تعداد تگ ها نمی تواند بیشتر از پنج عدد باشد',
);
return;
}
setValue('tags', e.target.value);
}}
value={data.tags}
label="تگ ها"
options={converItems(tags)}
multiple
onDelete={id => deleteTagHandler(id)}
error={errors.tags}
/>
</Grid>
</Grid>
)}
{tab === 1 && 'دومی'}
<Dump data={data} />
</Paper>
);
}
const mapStateToProps = createStructuredSelector({
tags: makeSelectTags(),
categories: makeSelectCategories(),
});
const withConnect = connect(
mapStateToProps,
null,
);
export default compose(withConnect)(UploadFileInfo);
Question Summary
If I understand your question correctly, you are asking how to guard the passing of options, options={converItems(tags)}, in the SelectBox in UploadFileInfo against null or undefined values when the data hasn't been fetch yet.
Solutions
There are a few options for either guarding against null or undefined values.
Easiest is to provide a default fallback value for tags. Here I am making an assumption the tags are an array, but they can be anything, so please adjust to match your needs.
Inline when passed options={converItems(tags || []) or options={converItems(tags ?? [])
In the function signature function UploadFileInfo({ tags = [], categories })
As part of a fallback return value in makeSelectTags
Another common pattern is conditional rendering where null may be the initial redux state value, so you simply wait until it is not null to render your UI.
Early return null if no tags
function UploadFileInfo({ tags, categories }) {
if (!tags) return null;
return (
<Paper square className={classes.paper}>
...
Conditional render SelectBox
{tags ? (
<SelectBox
...
/>
) : null
}
Side Note about fetching data calls in DashboardLayout
When you place function invocations directly in the function body they will be invoked any time react decides to "render" the component to do any DOM diffing, etc..., pretty much any time DashboardLayout renders the data fetches are made, which could have unintended effects. For this reason, react functional component bodies are supposed to be pure functions without side effects. Place any data fetching calls in an effect hook that is called only once when the component mounts (or appropriate dependency if it needs to be called under other specific conditions).
useEffect(() => {
getTagsFromServer();
getCategoriesFromServer();
}, []);
Use your functions to call the API inside React.useEffect.
All your API calls should be inside the useEffect hook.
For more on useEffect, read this
function DashboardLayout({
children,
showSideBar,
backgroundColor,
getTagsFromServer,
getCategoriesFromServer,
}) {
React.useEffect(() => {
getTagsFromServer();
getCategoriesFromServer();
}, []);
return (
<StyledDashbordLayout>
<NavBar />
<SideBarDrawer />
<Grid container className="container">
{showSideBar && (
<Grid item className="sidebar-section">
<SideBar />
</Grid>
)}
<Grid item className="content">
{children}
</Grid>
</Grid>
</StyledDashbordLayout>
);
}
Im new to react and I want to receive a list of items from an API using Axios in a react class, and then I want to use this info in a react function:
export class JobGetter extends Component {
state = {
Jobs: [],
};
componentDidMount() {
axios.get('/api/jobs/list-jobs', { headers: headers }).then(res => {
this.setState({
Jobs: res.data.map(Jobs => ({
value: Jobs.id,
title: Jobs.title,
companyName: Jobs.company_name,
InternalCode: Jobs.internal_code,
department: Jobs.department,
location: Jobs.location,
tags: Jobs.tags,
benefits: Jobs.benefits,
description: Jobs.description,
requirements: Jobs.requirements,
})),
}, () => {
console.log(this.state.Jobs);
});
});
}
render () {
return (
Jobs
)
}
}
export default function Jobs () {
const classes = useStyles();
return (
<div className='mainBox'>
<div className={classes.root}>
<ExpansionPanel style={{ backgroundColor: 'white' }}>
<ExpansionPanelSummary
expandIcon={<ExpandMoreIcon />}
aria-controls='panel1a-content'
id='panel1a-header'
style={{ backgroundColor: '#11ed7f' }}
>
<Typography className={classes.heading}>Job Opportunity</Typography>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<Container className='jobContainer'>
<Typography className={classes.TextHeaders}>
<Row>
<Col>Title:{Jobs.title} </Col>
<Col>Company Name:{Jobs.companyName} </Col>
<Col>Internal Code:{Jobs.InternalCode} </Col>
</Row>
.....
but my GET request doesn't function and I don't receive any info/show any info. what is the correct way to implement this?
With below changes, you should be able to get the data
JobGetter
render() {
return <Jobs jobs={this.state.Jobs} />;
}
Jobs
export default function Jobs ({ jobs }) {
OR
export default function Jobs (props) {
const { jobs } = props;
JobGetter
import Jobs from './Jobs'
export class JobGetter extends Component {
state = {
jobs: [],
};
componentDidMount() {
axios.get('/api/jobs/list-jobs', { headers: headers }).then(res => {
this.setState({
jobs: res.data.map(job => ({
value: job.id,
title: job.title,
companyName: job.company_name,
InternalCode: job.internal_code,
department: job.department,
location: job.location,
tags: job.tags,
benefits: job.benefits,
description: job.description,
requirements: job.requirements,
})),
}, () => {
console.log(this.state.jobs);
});
});
}
render () {
return (
<Jobs jobs={this.state.jobs} />
)
}
}
Jobs
export default function Jobs ({jobs}) {
const classes = useStyles();
return (
<div className='mainBox'>
<div className={classes.root}>
<ExpansionPanel style={{ backgroundColor: 'white' }}>
<ExpansionPanelSummary
expandIcon={<ExpandMoreIcon />}
aria-controls='panel1a-content'
id='panel1a-header'
style={{ backgroundColor: '#11ed7f' }}
>
<Typography className={classes.heading}>Job Opportunity</Typography>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<Container className='jobContainer'>
<Typography className={classes.TextHeaders}>
{jobs.map(job => (
<Row key={`job.value`}>
<Col>Title:{Jobs.title} </Col>
<Col>Company Name:{job.companyName} </Col>
<Col>Internal Code:{job.InternalCode} </Col>
</Row>)
)}
</Typography>
</Container>
</ExpansionPanelDetails>
</div>
</div>
Node: you are mixing functional components with classes.
I am making a Menu using React and Redux but am currently having an issue rendering the items when the user selects a category.
My goal is to render a list of items nested in the redux state of a particular category when it is clicked.
In Categories.js, it loads the categories from the redux store and displays them.
import React, { Component } from "react";
import { connect } from "react-redux";
import CategoryItems from "./CategoryItems"
import {
Card,
CardTitle,
CardImg,
Container,
Row,
Col,
CardBody,
Button
} from "shards-react";
class Categories extends Component {
handleClick = category => {
alert(category.title);
return (
<CategoryItems
category={category}
/>
);
};
render() {
let categoryList = this.props.categories.map(category => {
return (
<div key={category.id}>
<Col>
<Card
key={category.id}
onClick={() => {
this.handleClick(category);
}}
>
<CardBody>
<CardTitle>{category.title}</CardTitle>
</CardBody>
</Card>
</Col>
</div>
);
});
return (
<Container>
<Row>{categoryList}</Row>
</Container>
);
}
}
const mapStateToProps = state => {
return {
categories: state.categories,
};
};
export default connect(mapStateToProps)(Categories);
When a category is clicked, (the alert was just so I could make sure data made it) I have it set to render the items Array nested within the selected category.
import React, { Component } from 'react'
import {
Card,
CardTitle,
CardImg,
Container,
Row,
Col,
CardBody,
Button
} from "shards-react";
export class CategoryItems extends Component {
render() {
let items = this.props.category.items;
let categoryItems = items.map(item => {
return (
<Col className="">
<Card
className="mt-2 mb-2 item-col"
style={{ maxWidth: "500px" }}
key={item.id}
>
<CardBody>
<CardTitle style={{ position: "absolute", top: 20, right: 20 }}>
{item.title}
</CardTitle>
<CardImg
style={{ maxWidth: "200px" }}
src={item.img}
alt={item.title}
/>
<span
style={{ position: "absolute", bottom: 40, right: 100 }}
to="/"
// onClick={() => {
// this.handleClick(item.id);
// }}
>
<Button pill theme="info">
+
</Button>
</span>
<div style={{ position: "absolute", bottom: 40, right: 20 }}>
${item.price}
</div>
</CardBody>
</Card>
</Col>
);
});
return (
<Container className="menu-item-cont">
<Row>{categoryItems}</Row>
</Container>
);
}
}
export default CategoryItems
This part does not render the items and I don't get an error message.
I have also tried placing the items I would like rendered directly into the state just to see if I could get them to render on click similar to how the Categories.js is with no luck.
I am somewhat new to react so please forgive me if this is a beginner question.
Any help is appreciated as I have spent hours trying to figure this out.
The problem is that you're not rendering what gets returned from handleClick. What you need is a conditional render in the JSX, then determine whether or not to render the elements depending on a controlled flag variable in the state.
class Categories extends Component {
constructor() {
this.state = {
show: false,
category: undefined
}
this.toggle = (category) => {
this.setState({ show: !this.state.show, category })
}
}
render() {
let categoryList = this.props.categories.map(category => {
return (
<div key={category.id}>
<Col>
<Card
key={category.id}
onClick={e => this.toggle(category)}
>
<CardBody>
<CardTitle>{category.title}</CardTitle>
</CardBody>
</Card>
</Col>
</div>
);
});
return ([
<Container>
<Row>{categoryList}</Row>
</Container>,
(
this.state.show
? <CategoryItems category={this.state.category}/>
: null
)
]);
}
}
I am having a json file with data
{
data = [ {name: 'abc", label: "ABC",type: "hetero", path:"/assets/data/source1.jpg" },
{name: 'pqr", label: "PQR",type: "homo", path:"/assets/data/source2.jpg" },
{name: 'xyz", label: "XYZ",type: "hetero", path:"/assets/data/source3.jpg" },
]
}
This is the Parent class which is displaying the cards. On click of cards, It will navigate to another component which is DataForm.
class MainDataCard extends React.Component{
onClickForm = card => {
const {action} = this.props; // action value can be add/update. Currently action = "add"
this.props.history.push(`/user/${action}/${card.label}`);
};
render() {
{data.map((card, index) => {
return (
<Card
key={index}
className="cardStyle"
onClick={() => {
this.onClickForm(card);
}}
>
<CardContent className="cardContent">
<Grid container>
<Grid item md={12}>
<img src={card.path} alt="card" />
</Grid>
<Grid item md={12}>
<Typography color="textSecondary" gutterBottom>
{card.name}
</Typography>
</Grid>
</Grid>
</CardContent>
</Card>
)
}
}
In DataForm component, I am having a state for the form fields as:
state = {
dataForm: {
name: "",
type: this.tempData.type,
userName: "",
password: ""
},
tempData: {}
}
componentDidiMount(){
const {label} = this.props.match.params;
let temp ={};
{data.map((card) => {
if(data.label === label) {
temp =data;
}
})};
this.setState({ tempData : temp })
}
In DataForm , I want type field to have a default value as "hetero" or "homo". But there its showing an error as :
CANNOT READ PROPERTY TYPE OF UNDEFINED.
How can I get the default value in type field??
Thanks in advance.
Your component probably do not have access to this.tempData.type. Please provide full code of your components.