I m using React-js-highcharts, and i have done the below code, getting data e.render(): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.
error.
Kindly help me to sort out this.
Have followed this example.
https://github.com/whawker/react-jsx-highcharts/blob/gh-pages/examples/SimpleLine/App.js
import React from 'react';
import Highcharts from 'highcharts';
import {
HighchartsChart, Chart, withHighcharts, XAxis, YAxis, Title, Subtitle, Legend, LineSeries
} from 'react-jsx-highcharts';
import { extent as d3ArrayExtent } from 'd3-array';
const plotOptions = {
series: {
pointStart: 2010
}
};
class ProductHealth extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
}
}
componentDidMount() {
let defaultOptions = {
method:'GET',
headers:{
Accept: 'application/json',
}
};
fetch('http://localhost:3000/health', defaultOptions)
.then(function(response) {
// The response is a Response instance.
// You parse the data into a useable format using `.json()`
let resData = response.json();
return resData;
}).then(response => {
console.log("Response",response);
this.processResponseData(response);
}).catch(function(error){ console.log(error) })
}
processResponseData = (response) => {
let apiValues = [];
response.forEach(element => {
let value = [];
element.values.forEach(item => {
value.push(
[item.count]
);
})
apiValues.push({
name : element.api,
value : value
})
});
this.setState({
data : apiValues
});
};
renderLineChart() {
return this.state.data.map((lineData, index) => {
return (
<LineSeries key={index} name={lineData.api} data={lineData.value} />
)
})
}
render() {
if (this.state.data.length > 0) {
return (
<div>
<HighchartsChart plotOptions={plotOptions}>
<Chart />
<Title>Visa Direct Data</Title>
<Legend layout="vertical" align="right" verticalAlign="middle" />
<XAxis>
<XAxis.Title>Time</XAxis.Title>
</XAxis>
<YAxis>
<YAxis.Title>API Calls</YAxis.Title>
{this.renderLineChart()}
</YAxis>
</HighchartsChart>
</div>
);
} else {
return <div>Loading...</div>;
}
}
}
export default withHighcharts(ProductHealth, Highcharts);
I strongly recommend you to try to use our official React wrapper which is also available to download through npm.
Here is the link to package: https://www.npmjs.com/package/highcharts-react-official
GitHub repository: https://github.com/highcharts/highcharts-react
I know I'm 7 months late to this, but looks like the clue lies in
You may have returned undefined, an array or some other invalid object
You are returning this.state.data.map which will return an array.
I would suggest you try
renderLineChart () {
const { data } = this.state
return (
<Fragment>
{data.map((lineData, index) => (
<LineSeries key={index} name={lineData.api} data={lineData.value} />
)}
</Fragment>
)
}
Related
I am able to render each chart separately but I have no idea how to render multiple charts at the same time. Here is my code:
import React, { Component } from "react";
import {Line} from 'react-chartjs-2';
import axios from 'axios';
class Chart extends Component {
constructor(props) {
super(props);
this.state = {
chartData: {},
data: []
}
}
componentDidMount() {
axios.get('https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=2&page=1&sparkline=true')
.then(res => {
const x = res.data;
let y = [];
x.forEach(element => {
y.push(element.sparkline_in_7d.price)
})
console.log(y);
this.setState({
chartData: {
labels: y,
datasets:[{
data: y,
}
]
},
data: y
})
})
}
render() {
return (
<div className="chart">
//*WORKS*
{/* {this.state.data.map((n, index) => {
return (
<li key={index}>{n}</li>
);
})} */}
//*DOES NOT WORK*
{this.state.chartData.map((n, index) => {
return (
<Line key={index} data={n}/>
);
})}
</div>
);
}
}
export default (Chart);
Normally I don't have any issues mapping data but I am having issues doing so with chartjs as when I try this it says it is not a function. Any help here would be appreciated.
chartData should be an array.
Try the following approach.
//init state
this.state = {
chartData: [],
}
//service inside componentDidMount
axios.get('https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=2&page=1&sparkline=true')
.then(res => {
const x = res.data;
let chartData = [];
x.forEach(element => {
chartData.push({
labels: element.sparkline_in_7d.price,
datasets:[{data: element.sparkline_in_7d.price}]
});
});
this.setState({chartData});
})
Here is the fiddle.
https://codesandbox.io/s/sharp-wildflower-23hy7
Hope it will helps you.
I'm rendering a list of unread messages from facebook, it was working just fine last time I opened it, now it wouldn't render anything even though I'm getting a response from facebook !
import React, {Component} from 'react';
import { List, Button, Avatar, Icon } from 'antd';
const IconText = ({ type, text }) => (
<span>
<Icon type={type} style={{ marginRight: 8 }} />
{text}
</span>
);
class Notifications extends Component {
constructor(props) {
super(props);
this.state={
initLoading: true,
loading: false,
UnreadMessages:[],
pageAccessToken:""
};
}
componentWillMount(){
window.FB.api(
'/me',
'GET',
{"fields":"conversations{unread_count,messages{from,message}}","access_token":this.state.pageAccessToken},
function(response) {
console.log(response)
if (response.conversations) {
let listData = [];
for (let i = 0; i < response.conversations.data.length; i++) {
if(response.conversations.data[i].unread_count!==0){
listData.push({
from:response.conversations.data[i].messages.data[0].from.name,
message: response.conversations.data[i].messages.data[0].message,
})
}else i++;
}
this.setState({ UnreadMessages: listData });
}
else {
console.log(response.error);
}
}.bind(this)
);
}
render() {
const {UnreadMessages } = this.state;
return (
<div>
<List
itemLayout="horizontal"
dataSource={UnreadMessages}
loading={UnreadMessages.length ? false : true}
renderItem={item => (
<List.Item
actions={[<IconText type="facebook" />, <IconText type="message" />]}
>
<List.Item.Meta
avatar={<Avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />}
title={item.from}
description={item.message}
/>
</List.Item>
)}
/>
</div>
);
}
}
export default Notifications;
I really couldn't find the issue, I used the same structure for another list and it works just fine !
Try this code. I think the issue is in handling response directly inside window.FB.api call, so this.setState is not setting the state ,because this is not referring to the component.
componentWillMount(){
window.FB.api(
'/me',
'GET',
{"fields": "conversations{unread_count,messages{from,message}}", "access_token": this.state.pageAccessToken})
.then( (response) => {
console.log(response)
if (response.conversations) {
let listData = [];
for (let i = 0; i < response.conversations.data.length; i++) {
if(response.conversations.data[i].unread_count!==0){
listData.push({
from:response.conversations.data[i].messages.data[0].from.name,
message: response.conversations.data[i].messages.data[0].message,
})
}else i++;
}
this.setState({UnreadMessages: listData });
} else { console.log(response.error) }
})
Updated Version with arrow function to solve context issue - FB.api does not support promises, unfortunately. Also, else i++ does not make sense, i will get increased in the loop anyway:
componentWillMount() {
const {pageAccessToken} = this.state;
window.FB.api(
'/me',
{fields: 'conversations{unread_count,messages{from,message}}', access_token: pageAccessToken}, (response) => {
console.log(response);
if (response.conversations) {
let listData = [];
for (let i = 0; i < response.conversations.data.length; i++) {
if (response.conversations.data[i].unread_count !== 0) {
listData.push({
from: response.conversations.data[i].messages.data[0].from.name,
message: response.conversations.data[i].messages.data[0].message,
});
}
}
this.setState({UnreadMessages: listData});
} else {
console.log(response.error);
}
});
}
I´ve also improved some stuff (missing semicolons, unneccessary "GET", only single quotes, ...)
I am working on a React.js + D3.js project. I wanted App.js to fetch data from a json file and save this data into state and pass this parent sate data down to my child component state through the property. I found if I use static data in App.js works fine, but once fetching from a json file, it failed because no data can be stored into property. My App.js like this:
import React, { Component } from 'react';
import SandkeyGraph from './particle/SandkeyGraph';
class App extends Component {
state = {
data : null
}
// works fine in this way!
// state = {
// data: {
// "nodes":[
// {"node":0,"name":"node0"},
// {"node":1,"name":"node1"},
// {"node":2,"name":"node2"},
// {"node":3,"name":"node3"},
// {"node":4,"name":"node4"}
// ],
// "links":[
// {"source":0,"target":2,"value":2},
// {"source":1,"target":2,"value":2},
// {"source":1,"target":3,"value":2},
// {"source":0,"target":4,"value":2},
// {"source":2,"target":3,"value":2},
// {"source":2,"target":4,"value":2},
// {"source":3,"target":4,"value":4}
// ]}
// }
componentWillMount() {
this.getData('./data/sankey.json');
}
getData = (uri) => {
fetch(uri)
.then((response) => {
return response.json();
})
.then((data) => {
// successful got the data
console.log(data);
this.setState({ data });
});
}
render() {
// failed
const { data } = this.state;
return (
<div>
<SandkeyGraph
height={300}
width={700}
id="d3-sankey"
sankeyData = {this.state.data}
/>
</div>
);
}
}
export default App;
parrt of my is like this:
class SankeyGraph extends Component {
displayName: 'SankeyGraph';
state = {
sankeyData : null
}
constructor(props) {
super(props);
this.state.sankeyData = props.sankeyData || null;
}
PropTypes : {
id : PropTypes.string,
height: PropTypes.number,
width: PropTypes.number,
sankeyData : PropTypes.object,
}
componentDidMount () {
// will be null, if using fetch from App.js
//console.log(this.state.sankeyData);
this.setContext();
}
//...
Does anyone know how to handle this situation? Thank you so much in advanced!
After working out the problem, it turned out that there was no problem with fetch. It just didn't account for null in any of the components in the program (It would crash after using a null value.
For example in render:
render() {
if (this.state.data) {
return (
<div>
<SandkeyGraph
height={300}
width={700}
id="d3-sankey"
sankeyData = {this.state.data}
/>
</div>
);
}
else {
return <div/>
}
}
Or, the use of a ternary operator would work as well to be more concise (answer by #Eliran):
return (
{this.state.data ?
<div>
<SandkeyGraph
height={300}
width={700}
id="d3-sankey"
sankeyData = {this.state.data}
/>
</div> : <div>No Data Available</div>
);
You can add in your render function a condition:
render() {
// failed
const { data } = this.state;
return (
<div>
{data ?
<SandkeyGraph
height={300}
width={700}
id="d3-sankey"
sankeyData={data}
/> : "Loading..."
}
</div>
);
}
and only if data is populated the component will be rendered.
...
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
data : null
}
}
It seems like an error on the state declaration?
1.- Import your json in App component on top like this: import jsonData from './data/sankey.json'
2.- Set jsonData in state jsonData in App component.
constructor(props) {
super(props)
this.state = {
jsonData : {}
}
}
componentWillMount() {
this.setState({ jsonData })
}
You do not need to fetch as you have your json locally.
Once you have your json in your state, you can display it in render like this for example:
this.state.jsonData.data.links.map(x=>
<div>
<div>x.links.source</div>
<div>x.links.target</div>
</div>
)
I've been testing and you need to replace the getData() method to this:
getData = (uri) => {
fetch(uri, {
headers : {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
})
.then((response) => {
return response.json();
})
.then((data) => {
// successful got the data
console.log(data);
this.setState({ data });
});
}
This is because you need to declare 'Content-Type' and 'Accept' headers on your fetch options in order to make your request.
I am starting to learn React and creating my second project at the moment. I am trying to usi MovieDb API to create a movie search app. Everything is fine when I get the initial list of movies. But onClick on each of the list items I want to show the details of each movie. I have created a few apps like this using vanilla JS and traditional XHR call. This time I am using fetch API which seems straightforward ans simply to use, however when I map through response data to get id of each movie in order to retrieve details separately for each of them I get the full list of details for all the items, which is not the desired effect. I put the list of objects into an array, because after setState in map I was only getting the details for the last element. I know that I am probably doing something wrong within the API call but it might as well be my whole REACT code. I would appreciate any help.
My code
App.js
import React, { Component } from 'react';
import SearchInput from './Components/SearchInput'
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state =
{
value: '',
showComponent: false,
results: [],
images: {},
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleOnChange = this.handleOnChange.bind(this);
this.getImages = this.getImages.bind(this);
this.getData = this.getData.bind(this);
}
ComponentWillMount() {
this.getImages();
this.getData();
}
getImages(d) {
let request = 'https://api.themoviedb.org/3/configuration?api_key=70790634913a5fad270423eb23e97259'
fetch(request)
.then((response) => {
return response.json();
}).then((data) => {
this.setState({
images: data.images
});
});
}
getData() {
let request = new Request('https://api.themoviedb.org/3/search/movie?api_key=70790634913a5fad270423eb23e97259&query='+this.state.value+'');
fetch(request)
.then((response) => {
return response.json();
}).then((data) => {
this.setState({
results: data.results
});
});
}
handleOnChange(e) {
this.setState({value: e.target.value})
}
handleSubmit(e) {
e.preventDefault();
this.getImages();
this.setState({showComponent: true});
this.getData();
}
render() {
return (
<SearchInput handleSubmit={this.handleSubmit} handleOnChange={this.handleOnChange} results={this.state.results} images={this.state.images} value={this.state.value} showComponent={this.state.showComponent}/>
);
}
}
export default App;
SearchInput.js
import React, {Component} from 'react';
import MoviesList from './MoviesList';
class SearchInput extends Component {
render() {
return(
<div className='container'>
<form id='search-form' onSubmit={this.props.handleSubmit}>
<input value={this.props.value} onChange={this.props.handleOnChange} type='text' placeholder='Search movies, tv shows...' name='search-field' id='search-field' />
<button type='submit'>Search</button>
</form>
<ul>
{this.props.showComponent ?
<MoviesList value={this.props.value} results={this.props.results} images={this.props.images}/> : null
}
</ul>
</div>
)
}
}
export default SearchInput;
This is the component where I try to fetch details data
MovieList.js
import React, { Component } from 'react';
import MovieDetails from './MovieDetails';
let details = [];
class MoviesList extends Component {
constructor(props) {
super(props);
this.state = {
showComponent: false,
details: []
}
this.showDetails = this.showDetails.bind(this);
this.getDetails = this.getDetails.bind(this);
}
componentDidMount() {
this.getDetails();
}
getDetails() {
let request = new Request('https://api.themoviedb.org/3/search/movie?api_key=70790634913a5fad270423eb23e97259&query='+this.props.value+'');
fetch(request)
.then((response) => {
return response.json();
}).then((data) => {
data.results.forEach((result, i) => {
let url = 'https://api.themoviedb.org/3/movie/'+ result.id +'?api_key=70790634913a5fad270423eb23e97259&append_to_response=videos,images';
return fetch(url)
.then((response) => {
return response.json();
}).then((data) => {
details.push(data)
this.setState({details: details});
});
});
console.log(details);
});
}
showDetails(id) {
this.setState({showComponent: true}, () => {
console.log(this.state.details)
});
console.log(this.props.results)
}
render() {
let results;
let images = this.props.images;
results = this.props.results.map((result, index) => {
return(
<li ref={result.id} id={result.id} key={result.id} onClick={this.showDetails}>
{result.title}{result.id}
<img src={images.base_url +`${images.poster_sizes?images.poster_sizes[0]: 'err'}` + result.backdrop_path} alt=''/>
</li>
)
});
return (
<div>
{results}
<div>
{this.state.showComponent ? <MovieDetails details={this.state.details} results={this.props.results}/> : null}
</div>
</div>
)
}
}
export default MoviesList;
MovieDetails.js
import React, { Component } from 'react';
class MovieDetails extends Component {
render() {
let details;
details = this.props.details.map((detail,index) => {
if (this.props.results[index].id === detail.id) {
return(
<div key={detail.id}>
{this.props.results[index].id} {detail.id}
</div>
)} else {
console.log('err')
}
});
return(
<ul>
{details}
</ul>
)
}
}
export default MovieDetails;
Theres a lot going on here...
//Here you would attach an onclick listener and would fire your "get details about this specific movie function" sending through either, the id, or full result if you wish.
//Then you getDetails, would need to take an argument, (the id) which you could use to fetch one movie.
getDetails(id){
fetch(id)
displayresults, profit
}
results = this.props.results.map((result, index) => {
return(
<li onClick={() => this.getDetails(result.id) ref={result.id} id={result.id} key={result.id} onClick={this.showDetails}>
{result.title}{result.id}
<img src={images.base_url +`${images.poster_sizes?images.poster_sizes[0]: 'err'}` + result.backdrop_path} alt=''/>
</li>
)
});
Thanks for all the answers but I have actually maanged to sort it out with a bit of help from a friend. In my MovieList I returned a new Component called Movie for each component and there I make a call to API fro movie details using each of the movie details from my map function in MovieList component
Movielist
import React, { Component } from 'react';
import Movie from './Movie';
class MoviesList extends Component {
render() {
let results;
if(this.props.results) {
results = this.props.results.map((result, index) => {
return(
<Movie key={result.id} result={result} images={this.props.images}/>
)
});
}
return (
<div>
{results}
</div>
)
}
}
export default MoviesList;
Movie.js
import React, { Component } from 'react';
import MovieDetails from './MovieDetails';
class Movie extends Component {
constructor(props) {
super(props);
this.state = {
showComponent: false,
details: []
}
this.showDetails = this.showDetails.bind(this);
this.getDetails = this.getDetails.bind(this);
}
componentDidMount() {
this.getDetails();
}
getDetails() {
let request = new Request('https://api.themoviedb.org/3/search/movie?api_key=70790634913a5fad270423eb23e97259&query='+this.props.value+'');
fetch(request)
.then((response) => {
return response.json();
}).then((data) => {
let url = 'https://api.themoviedb.org/3/movie/'+ this.props.result.id +'?api_key=70790634913a5fad270423eb23e97259&append_to_response=videos,images';
return fetch(url)
}).then((response) => {
return response.json();
}).then((data) => {
this.setState({details: data});
});
}
showDetails(id) {
this.setState({showComponent: true}, () => {
console.log(this.state.details)
});
}
render() {
return(
<li ref={this.props.result.id} id={this.props.result.id} key={this.props.result.id} onClick={this.showDetails}>
{this.props.result.title}
<img src={this.props.images.base_url +`${this.props.images.poster_sizes?this.props.images.poster_sizes[0]: 'err'}` + this.props.result.backdrop_path} alt=''/>
{this.state.showComponent ? <MovieDetails details={this.state.details}/> : null}
</li>
)
}
}
export default Movie;
I have this component Cat that pulls the data from a local json file and displays all the Cats from the file in the alphabetical order. I need to display first 10 cats, and then have a Load More button to display the rest. Does anyone have a solution on how to do it in a good way? The line {providerNumber.length} Cats still needs to show the total number of cats, not the first 10.
Thank you!
import React, { Component } from 'react';
import { NavLink } from 'react-router-dom';
import Error from './Error.jsx';
export default class Cat extends React.Component{
constructor() {
super();
this.state = {
providersData: [],
loading: true
};
}
componentDidMount () {
setTimeout(() => this.setState({ loading: false }), 500);
fetch('../feed/sample.json')
.then(response => { console.log(response); return response.json()})
.then(responseData => {
console.log(responseData)
this.setState({ providersData: [...responseData.providers].sort((a,b) => {
const aName = a.companyName.toUpperCase()
const bName = b.companyName.toUpperCase()
if (aName < bName) {
return -1;
}
if (aName > bName) {
return 1
}
// names must be equal
return 0
})
});
})
.catch(error => {
console.log('Error fetching and parsing data', error);
});
}
render() {
const { loading } = this.state;
const providerNumber = this.state.providersData.filter(provider => provider.yearStarted >= 2010 && provider.type === 'cat')
if(loading) {
return (
<div> <img src="./../assets/loader.svg" alt=""/></div>
); // render loading when app is not ready
}
return this.state.providersData.length ? (
<div>
<h1>Cats</h1>
<div> {providerNumber.length} Cats</div>
{this.state.providersData.map(function(provider, index) {
if (provider.yearStarted >= 2010 && provider.type === 'cat') {
return (
<div key={index} className="job">
<h2>{provider.companyName}</h2>
<img src={provider.images['Company Logo'].url} alt=""/>
</div>
)
}
})}
</div>
) : <Error />
}
};
You can do something like this before you call .map in your render:
this.state.providersData.slice(0, this.state.numberOfCatsShown).map(...)
You would need to initialize this.state.numberOfCatsShown to 10 in your constructor first.
When you want to display more cats, use a function that looks like this:
showMoreCats() {
const newNumberOfCatsShown = this.state.numberOfCatsShown + 10;
// set it to the length of the array to show all the cats.
this.setState({ numberOfCatsShown: newNumberOfCatsShown });
}