I'm very new in React and JS. I'm trying to create a graph from JSON, but I can't pass value to graph with MAP function and PUSH, my result is just numbers that I put manually (2, 3, 5 - Line 45)
dataGraf.push(hit.minutos) //Works well
this.state.items.map((hit) => (dataGraf.push(hit.minutos))) //No erros, but no data add
My JSON:
[
{"indice":1,"minutos":569,"programa":"seg"},
{"indice":2,"minutos":421,"programa":"ter"},
{"indice":3,"minutos":258,"programa":"quar"}
]
import React,{Component} from 'react';
import Chart from "chart.js";
import Config from '../components/config.js';
let dataGraf = []
class GraphChart extends Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
this.state = {
items: [],
error: null
};
}
componentDidMount() {
let url1 = `${Config.FullURL}/progativo`
const ctx = this.ctx;
fetch(url1)
.then(items => items.json())
.then((items) => {
this.setState({ items });
});
this.funDadosGrafico()
new Chart(ctx, {
type: "pie",
data: {
labels: ['blue', 'green','red'],
datasets: [
{
label: "# of Likes",
data: dataGraf
}
]
}
});
} //End of componentDidMount
funDadosGrafico(){
dataGraf.push(2, 5, 3) //Just for test
this.state.items.map((hit) => (
dataGraf.push(hit.minutos)))
}
render() {
return (
<div>
<canvas width='800' height='300' ref={ctx => (this.ctx = ctx)}/>
</div>
)
}
}
export default GraphChart;
try moving the execution this.funDadosGrafico() and Canvas creation to componentDidUpdate.
once you call fetch, fetch wont block the thread, hence this.funDadosGrafico() is executed right aftwards. then blocks are executed only after fetch is resolved.
plus, setState method is also async. it is not going to work as expected even if you remove setState from fetch call like:
this.setState(updateMyState)
this.funDadosGrafico() // not going to work also, setState is async
Related
Getting undefined data type error while fetching data from JSON
I have searched at many places but didn't get the suitable answer
import SavedData from "./SavedData";
export default class Saved extends React.Component {
constructor() {
super();
this.state = {
loading: true,
datas: [],
};
}
async componentDidMount() {
const url = "https://todo-list-site.herokuapp.com/todo-data";
const response = await fetch(url);
const todoData = response.json().then((res) => {
this.setState({ datas: res });
});
}
render() {
console.log(this.state.datas[0].description); //not able to get data
return (
<div>
{/* {this.state.datas.map((items) => (
<SavedData
key={items.countTodo}
title={items.title}
desc={items.desc}
/>
))} */}
</div>
);
}
}
Someone help me so that I can proceed
Just like Dave Newton has pointed out in the comments, the render is triggered before the request completes. This is normal and you just need to handle it properly.
If you see the console logs of this codesandbox, you can see that initially this.state.datas is just an empty array [] - so any attempt to access this.state.datas[0].description will be undefined. Only after the state is updated when the request completes, the logs show the data retrieved - this is because according to the mount lifecycle of a React Component, the render() is called before the componentDidMount() and also the request being async.
This is very common and it is even recommended by the official React docs to make HTTP calls in componentDidMount(). The docs also has provided an example to handle this issue.
import SavedData from "./SavedData";
export default class Saved extends React.Component {
constructor() {
super();
this.state = {
loading: true, // we initially set this to true
datas: [],
};
}
async componentDidMount() {
const url = "https://todo-list-site.herokuapp.com/todo-data";
const response = await fetch(url);
const todoData = response.json().then((res) => {
this.setState({
datas: res,
loading: false // when the request is complete, we set this to false
});
});
}
render() {
if (this.state.loading) {
// during the first render, loading will be true and we
// can return a loading message or a spinner
return (
<div>Loading...</div>
);
}
// when render is called after the state update, loading will be false
// and this.state.datas will have the fetched data
console.log(this.state.datas[0].description);
return (
<div>
{this.state.datas.map((items) => (
<SavedData
key={items.countTodo}
title={items.title}
desc={items.desc}
/>
))}
</div>
);
}
}
Your datas state is initially an empty array until your componentDidMount fires and sets the state. As a result, your console log will then be undefined until the state is set. In order to combat this you must wait for this.state.datas[0] to be true before accessing the first objects description within the array. The following code seems to work as expected
import React from "react";
export default class Saved extends React.Component {
constructor() {
super();
this.state = {
loading: true,
datas: []
};
}
async componentDidMount() {
const url = "https://todo-list-site.herokuapp.com/todo-data";
const response = await fetch(url);
response.json().then((res) => {
this.setState({ datas: res });
});
}
render() {
console.log(this.state.datas[0] && this.state.datas[0].description);
return (
<div>
{this.state.datas.map((items, i) => (
<div key={i}>
<div> title={items.title}</div>
<div> desc={items.description}</div>
</div>
))}
</div>
);
}
}
I created a class to get api.
export default class ProductDetail extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
product : []
}
}
componentDidMount() {
this.getProductRequest();
}
...
then create getProductRequest function:
async getProductRequest() {
let response = await fetch('https: ...
let json = await response.json();
console.log(json);
this.setState({ product : json.data});
}
the console result is:
{id: 225782, title: "test", images: Array(1), price: "1$"}
Now in render i get same result:
render() {
console.log(this.state.product);
return (...
Now I try to read params:
render() {
console.log(this.state.product.title);
return (...
But I get this error:
TypeError: Cannot read property 'title' of underfined
what's the wrong?
Edit: Structure:
export default class ProductDetail extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
product : []
}
}
componentDidMount() {
this.getProductRequest();
}
render() {
console.log(this.state.product.title);
return (
<View> <Text style={styles.name}>title</Text></View>
);
}
async getProductRequest() {
try {
let id = this.props.navigation.state.params.productId;
let response = await
fetch('https://www.example.com/product', {
method : 'POST',
headers : {
'Accept' : 'application/json',
'Content-Type' : 'application/json'
},
body : JSON.stringify({
id
})
});
let json = await response.json();
//json: {"data":{id: 225782, title: "test", images: Array(1), price: "1$"},"status":"success"}
this.setState({ product : json.data});
} catch(error) {
//console.log(error)
}
}
}
...
Because, componentDidMount() re-render after the first execution of rendering. So, when you are putting console.log(this.state.product.title); in the render before return, it doesn't get the title param first time.
After the re-render, the value will be available. So, if you want to check the output put console.log elsewhere or just remove it
Edit
You can call this.getProductRequest(); in componentWillMount() instead of componentDidMount()
componentWillMount() {
this.getProductRequest();
}
let product = JSON.parse(this.state.product
if(product.title){
console.log(product.title)
}
Let with above code. If you are getting string in your state, it may create an issue. Let me know if its work.
As said react official documentation :
componentDidMount() is invoked immediately after a component is mounted (inserted into the tree)
it does mean that first time your render method is unable to read the title of your product (first time that your render method is invoked, this.state.product is still an empty array). I suggest you to check if your array is empty
render() {
if (this.state.product) {
return (
<Text>Loading...</Text>
} else
return (
<View><Text>{this.state.product.title}</Text></View>
)
}
Don't use componentWillMount() because these methods are considered legacy and you should avoid them in new code.
componentWillMount()
If your render function actually does look like you posted, then this can't work. Try chaning your render function to something like this.
render() {
const { product } = this.state
if (!product || !product.title) {
return null
}
return (
<View><Textstyle={styles.name}>product.title</Text></View>
)
}
I am trying to fetch some data, which is in the form:
[
{
"id": 1,
"some_data": "..."
},
...
]
What I am trying to get is a list displaying the items from the fetch. If I put the same data in a file within the project, it works.
However when I tried to map it, I got an error saying "this.data.map is not a function". So I changed it a bit by using Array.from(). It currently looks like this:
export default class Main extends React.Component {
constructor(props) {
super(props);
this.state = {
items = [];
};
this.getData = this.getData.bind(this);
}
getData = () => {
fetch("URL",{
method: "get",
header: { "Content-Type": "application/json" }
})
.then(response => {
var array = Array.from(response.json())
this.setState({items: array});
})
}
render() {
const list = this.state.items.map((r, i) => {
return (
<Item
id = { r[i].id }
some_data = { r[i].some_data }
...
/>
)
})
return(
<div>
<Item
p = {list}
>
</div>
)
}
}
First of all no state is neede to store the response. Its happening due to the state value is not reflecting in your render.
Call a function inside success response & map the response inside the function & set State there.
OR
Put the below code outside render function assigning to variable like below
const list = this.state.items.map((r, i) => {
return (
)
})
return(
)
}
render () {
{list}
}
Try something like this.....
It's better to load the data once component is mounted. Also, there's no URL, I'm assuming that you've hidden this.
Once you 'see' what's in response, you can code against that accordingly.
export default class Main extends React.Component {
constructor(props) {
super(props);
this.state = {
items = [];
};
// this.getData = this.getData.bind(this);
}
componentDidMount(){
// Attempt to load data once component mounted.
this.getData();
}
getData = () => {
// Don't you need the URL below, or have you deliberately hidden it?
fetch("URL",{
method: "get",
header: { "Content-Type": "application/json" }
})
.then(response => {
console.log(response); // See exactly what is in response....
var array = Array.from(response.json())
console.log(array); // Check array is really what you want
// You could try a JSON.Parse....
var jsonArray = JSON.Parse(response);
console.log(jsonArray);
this.setState({items: array});
})
}
render() {
const list = this.state.items.map((r, i) => {
return (
<Item
id = { r[i].id }
some_data = { r[i].some_data }
...
/>
)
})
return(
<div>
<Item
p = {list}
>
</div>
)
}
}
I'm trying to call a localhost API that i created in my react app class. This API will return a list of json data, i'm trying to save these results in a property
I don't know much about Reacjs. What i have tried so far is to create a method that will call the API and return the data, the i call this method in my class and save the results in a property.
The type of this method is Promise since the results that i'm expectibng are a list of data :
let items: any[];
function getIncidentsFromApiAsync(): Promise<any[]>{
return fetch('http://localhost:3978/calling')
.then((response) => response.json())
}
export class App extends React.Component<{}, IDetailsListCustomColumnsExampleState> {
constructor(props: {}) {
super(props);
getIncidentsFromApiAsync().then(json => items = json);
}
}
I haven't been able to see the results since items is always undefined after calling getIncidentsFromApiAsync() method.
You can handle this in React using State and lifecycle method componentDidMount that gets called when the component is ready:
function getIncidentsFromApiAsync(): Promise<any[]>{
return fetch('http://localhost:3978/calling').then(
(response) => response.json()
);
}
export class App extends React.Component<{}, IDetailsListCustomColumnsExampleState> {
constructor(props: {}) {
super(props);
this.state = {
items: []
};
}
componentDidMount() {
getIncidentsFromApiAsync().then(json => this.setState({ items: json });
}
render() {
if (this.state.items.length) {
const itemsList = this.state.items.map((item) => <li key={item}>{item}</li>);
return (
<div>
<ul>{itemsList}</ul>
</div>
);
}
return <div>List is not available</div>;
}
}
I have a json file named autofill.json and it's created to autofill a search bar when pressed on.
the autofill.json is a test file that's why it looks like this.
[
{
"a": {
"apple": {
"name": "apple",
"href": "https://www.apple.com/"
},
"armadillo": {
"name": "armadillo",
"href": "https://www.armadillo.com/"
}
},
"b": {
"box": {
"name": "apple",
"href": "https://www.berserk.com/"
},
"berserk": {
"name": "berserk",
"href": "https://www.berserk.com/"
}
}
}
]
The .json file is then fetched in the file named FetchAndParseResults.js
import fetch from 'isomorphic-fetch'
const FetchAndParseResults = (url) => {
return fetch(url).then(response => {
const parsedJson = response.json()
return parsedJson
})
}
export default FetchAndParseResults
The data that gets fetched is used in searchcontainer.js where everything gets placed in, the search etc.
import React from 'react'
import Searchbar from './index.js'
import FetchAndParseResults from './FetchAndParseResults.js'
class SearchContainer extends React.Component {
state = {
results: []
}
performSearch = event => {
return FetchAndParseResults('static/autofill.json').then(data => {
this.setState({ results: data })
})
}
render () {
console.log('performSearch event', this.performSearch)
console.log('data inside performSearch', this.state.results)
return (
<Searchbar
performSearch={this.performSearch}
results={this.state.results}
/>
)
}
}
export default SearchContainer
Then to map through the data that is in autofill.json there is a file named autofill.js
import React from 'react'
import PropTypes from 'prop-types'
import Styles from './searchbar.scss'
const AutoFill = (props) => {
console.log('proppppppsss', props)
const results = props.results || []
return (
<ul className={Styles.searchUl}>
{results.map(({ name, href }) => (
<li className={Styles.searchLi} key={href}>
<a className={Styles.searchA} href={href} target='_blank' rel='noopener noreferrer' key={href}>
{name}
</a>
</li>
))}
</ul>
)
}
AutoFill.propTypes = {
results: PropTypes.array
}
export default AutoFill
the Searchbar component in (index.js) that is being used in searchcontainer.js
import React from 'react'
import Styles from './searchbar.scss'
import Icon from '../../components/icon/icon'
import Search from '../../components/form-input/search'
import AutoFill from './autofill'
import PropTypes from 'prop-types'
export default class Searchbar extends React.Component {
constructor (props) {
super(props)
this.state = {
className: Styles.input,
icon: Styles.icon__wrapper,
value: []
}
this.input = React.createRef()
}
openInput = () => {
this.setState({
className: Styles.input__active,
icon: Styles.iconWidth
}, () => {
this.input.focus()
})
this.props.onOpen && this.props.onOpen()
}
closeInput = () => {
this.setState({
className: Styles.input,
icon: Styles.icon__wrapper
})
this.props.onClose && this.props.onClose()
}
handleChange = event => {
let value = event.target.value
this.setState({ value })
this.props.performSearch(value)
}
handleSubmit = event => {
event.preventDefault()
}
render () {
console.log('results', this.props.results)
console.log('state.value', this.state.value)
return (
<div>
<form onSubmit={this.handleSubmit} className={Styles.search}>
<div className={this.state.icon}>
<Icon className={Styles.icon__wrapper} iconName='faSearch' onClick={this.openInput} />
</div>
<Search autoComplete='off' value={this.state.value} onChange={this.handleChange} id='search' tabIndex='0' myref={input => { this.input = input }} className={this.state.className} onBlur={this.closeInput} placeholder='Search' />
</form>
<div>
<AutoFill results={this.props.results} />
</div>
</div>
)
}
}
Search.propTypes = {
performSearch: PropTypes.func,
results: PropTypes.array
}
When i try to refer to a what is in the json file from the search i receive the error,
GET http://localhost:3000/[object%20Object] 404 (Not Found)
And
about:1 Uncaught (in promise) SyntaxError: Unexpected token < in JSON
at position 0
The second error is fixed by doing
const parsedJson = response.text(
instead of
const parsedJson = response.json()
to get more information where/what the error takes place. But by doing this i receive the error,
searchcontainer.js:12 Uncaught (in promise) TypeError: Cannot read property 'results' of undefined
I've tried to run it from npm build instead of running it in a dev environment which didn't fix it.
I read that a mock url should work but then again i want to acces it from a file and not from a url?
Any help would be highly appreciated and looked into.
The problem is most likely in the fetch call. If you look at the error message GET http://localhost:3000/[object%20Object] 404 (Not Found)
You can see that it is trying to append an object to the URL localhost:3000/.
You are getting the Unexpected token < in JSON at position 0 error because the response of your fetch request is probably a 404 page. The < is most likely the first char of <html>
To access the JSON object in your React files, you can simply do an importation like so;
import * as autofillData from 'autofill.json';
It will be returned as a JSON object.
I believe you are using the isomorphic-fetch package wrongly, if you look at their source code, https://github.com/matthew-andrews/isomorphic-fetch/blob/master/fetch-npm-node.js#L5 , they are accepting a URL to make a call to the API URL which will return a promise or a JSON object depending on the implementation of the API that you are calling.
If you were to dive deeper into the open-source code here (https://github.com/matthew-andrews/isomorphic-fetch/blob/master/fetch-npm-node.js#L8) , you will notice that isomorphic-fetch package is using another package node-fetch to do their fetch call, which accepts the API URL and the method request options to call the API with. (As stated here; https://github.com/bitinn/node-fetch/blob/master/src/index.js#L34)
To continue with your test, perhaps this might be the solution you'd prefer?
import fetch from 'isomorphic-fetch';
import * as autofillData from 'autofill.json'; //test data
const FetchResults = event => {
return fetch('/https://jsonplaceholder.typicode.com/todos/1'') //mockURL, to be replaced with real API
.then(response => {
// const parsedJson = response.json(); // TODO: un-comment this line when the real API url is usable
const parsedJson = autofillData; // TODO: remove this line when mocking is done and the real API URL is ready
return parsedJson;
})
}
export default FetchResults;
To have a mock URL placeholder, I would suggest https://jsonplaceholder.typicode.com/ to prevent your fetch result to return an unexpected error during test mocking.
Hope this is helpful.
The question has been solved, The main issue was with defining const names such as const results = [] which should've been const results = props.results || [].
The code has been updated incase you have problems aswell.