I used react.js to connect the api.
And i want to show the identifier on the page.
Here is response to console.log(this.state.weathers.cwbopendata)
After i console.log(this.state.weathers.cwbopendata.identifier)
,I got the error
What should i do to show the identifier on the page?
Here is the code:
import React,{Component} from 'react';
class App extends Component {
constructor(){
super();
this.state = {
weathers: {},
};
}
componentDidMount(){
fetch('https://opendata.cwb.gov.tw/fileapi/v1/opendataapi/F-C0032-001?Authorization=CWB-BB78764B-9687-4C1C-B180-66CB616129E5&format=JSON')
.then(response=> response.json())
.then( JSON=> this.setState({weathers:JSON}))
}
render(){
return (
<div className="App">
{console.log(this.state.weathers.cwbopendata.identifier)}
</div>
);
}
}
export default App;
This is a classic problem many newcomers face. You need to add a state to let your component know that data fetching is in progress, is completed or there is an error. So the component can show real data when it successfully fetched it, until then something you can display to the UI to let users know that the App is fetching data... I'd write it something like:
import React, { Component } from "react";
class App extends Component {
constructor() {
super();
this.state = {
weathers: {},
isFetching: true
};
}
componentDidMount() {
fetch(
"https://opendata.cwb.gov.tw/fileapi/v1/opendataapi/F-C0032-001?Authorization=CWB-BB78764B-9687-4C1C-B180-66CB616129E5&format=JSON"
)
.then(response => response.json())
.then(json => this.setState({ weathers: json, isFetching: false }));
}
render() {
const { isFetching, weathers } = this.state;
return (
<div className="App">
{isFetching ? "Loading.." : weathers.cwbopendata.identifier}
</div>
);
}
}
export default App;
In your case you tried to render the data at first mount, and at this point of time weathers is just holding an empty object {}. Thus weathers.cwbopendata returns undefined, and undefined.identifier throws the error as you see in the browser console.
Initially render method is being called before componentDidMount.
By the time it was called, data hadn't been fetched yet.
So you should properly handle situation when you have empty state.
import React,{Component} from 'react';
class App extends Component {
constructor(){
super();
this.state = {
weathers: {},
};
}
componentDidMount(){
fetch('https://opendata.cwb.gov.tw/fileapi/v1/opendataapi/F-C0032-001?Authorization=CWB-BB78764B-9687-4C1C-B180-66CB616129E5&format=JSON')
.then(response=> response.json())
.then( JSON=> this.setState({weathers:JSON}))
}
render(){
return (
<div className="App">
{console.log(this.state.weathers.cwbopendata && this.state.weathers.cwbopendata.identifier)}
</div>
);
}
}
export default App;
Related
I'm trying to call a dictionary from Django Rest Framework API to view on my frontend. Using Django backend & Reactjs frontend. Through some research looks like i'm getting this error due to the map() function only accepting arrays, while my API is returning a dictionary (I THINK SO).
How do I fix this? I'm new to javascript & apologies in advance for the messy code. Please see my App.js below:
App.js
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
todoList: [],
}
this.fetchTasks = this.fetchTasks.bind(this)
};
componentWillMount() {
this.fetchTasks()
}
fetchTasks() {
fetch('http://127.0.0.1:8000/api/api-overview')
.then(response => response.json())
.then(data =>
this.setState({
todoList: data
})
)
}
render() {
var tasks = this.state.todoList
return (
<div className="container">
{tasks.map(function (task, index) {
return (
<div className="center-column">
<div className="item-row">
<div key={index} className="centered">
<span>{task.bitcoin_symbol}</span>
</div>
</div>
</div>
)
})}
</div>
);
}
}
export default App;
API response:
You're fetching a single object, not an array. .map() is a method which run over iterables(arrays, strings, etc - objects, that can be iterated over) and creates a new output element from each input one. In react we mainly use it to convert an item to its JSX(react/html) representation. As you're working over a single object, you should access it directly:
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
bitcoinData = null
}
this.fetchBitcoinData = this.fetchBitcoinData.bind(this);
};
componentWillMount() {
this.fetchBitcoinData();
}
fetchBitcoinData() {
fetch('http://127.0.0.1:8000/api/api-overview')
.then(response => response.json())
.then(data =>
this.setState({
bitcoinData: data
});
);
}
getBitcoinRepresentation() {
var bitcoinData = this.state.fetchBitcoinData;
if (!bitcoinData) {
return <div>Loading...</div>;
}
else {
return (
<div className="container">
<div>{bitcoinData.bitcoin_symbol}</div>
<div>{bitcoinData.bitcoin_price}</div>
<div>{bitcoinData.bitcoin_dailychangeinprice}</div>
</div>
);
}
}
render() {
return getBitcoinRepresentation();
}
}
export default App;
This was my code
import React, { Component } from "react";
import axios from "axios";
class App extends Component {
state = {
invites: [],
};
constructor() {
super();
axios.get(`http://localhost:8080/security/allUser`).then((res) => {
console.log(res.data);
this.setState({ invites: res.data });
});
}
render() {
return (
<div>
{this.state.invites.map((invite) => (
<h2 key={invite.id}>{invite.name}</h2>
))}
<h1>Welcome</h1>
</div>
);
}
}
export default App;
state and setState have worked for me alright for more complex codes before. This one keeps showing the same error
This is the error:
index.js:1 Warning: Can't call setState on a component that is not yet mounted. This is a no-op, but it might indicate a bug in your application. Instead, assign to this.state directly or define a state = {}; class property with the desired state in the App component.
Add a componentDidMount() and write your request call inside it. When the component first loads the componentDidMount function will run.
Actually you can make request in constructor (React allows it but you shouldnt) but you have to make the request only after the component has been mounted or just before it is about to be mounted.
So it is wise to make your requests in componentDidMount().
import React, { Component } from "react";
import axios from "axios";
class App extends Component {
constructor(props) {
super(props);
this.state = {
invites: [],
};
}
componentDidMount() {
axios.get(`http://localhost:8080/security/allUser`).then((res) => {
console.log(res.data);
this.setState({ invites: res.data });
});
}
render() {
return (
<div>
{this.state.invites.map((invite) => (
<h2 key={invite.id}>{invite.name}</h2>
))}
<h1>Welcome</h1>
</div>
);
}
}
export default App;
Yo guys, getting error 'contacts.map is not a function' not sure why is that ? just starting in react maybe missing something obvious. I'm getting the data when I console log all good.
code below:
import React, { Component } from 'react'
import axios from 'axios';
class Contacts extends Component {
constructor(){
super();
this.state = {
contacts: [],
}
}
componentDidMount(){
axios.get('url')
.then(response => {
this.setState({ contacts: response.data });
})
.catch(function (error) {
console.log(error);
})
}
render() {
const { contacts } = this.state
return(
<div>
{contacts.map(contact => (
<h1>contact.hello</h1>
))}
</div>
)
}
}
export default Contacts;
Apparently its an object not an array...
How can i render this object then?
It has one property for now but will have more later on: tried JSON.stringify(obj)
{hello: "test"}
The problem is that you set contacts to response.data, which evidently it's not an array.
componentDidMount fires after the component is mounted and tries to get the string 'url'. When state is updated, the component is redrawn and it gives the error.
Since the contacts is an object I would recommend you to do Object.keys and then .map on it so that you can get object keys and it’s values.
One more thing never forget to add unique key to the parent jsx element when you iterate array of data or an object like below.
<div>
{Object.keys(contacts).map((name, index) => (
<h1 key={'Key'+index}>{contacts[name]}</h1>
))}
</div>
From react docs:
Note:
These methods are considered legacy and you should avoid them in new code:
UNSAFE_componentWillMount()
When you want to wrap an object you can simply wrap it in brackets
class Contacts extends Component {
constructor() {
super();
this.state = {
contacts: [],
}
}
componentDidMount() {
axios.get('url')
.then(({ data }) => {
this.setState({ contacts: [data] });
})
.catch((error) => {
console.log(error);
});
}
render() {
const { contacts } = this.state;
return (
<div>
{contacts.map(contact => (
<h1 key={/* unique key */}>contact.hello</h1>
))}
</div>
);
}
}
Use async await to get the response before the component is mounted
import React, { Component } from 'react'
import axios from 'axios';
class Contacts extends Component {
constructor(){
super();
this.state = {
contacts: [],
}
}
async componentWillMount(){
const response = await axios.get('url')
this.setState({ contacts: response.data })
}
render() {
const { contacts } = this.state
return(
<div>
{contacts.map(contact => (
<h1>contact.hello</h1>
))}
</div>
)
}
}
export default Contacts;
Iam trying to understand and learn how to pass around data as props to other components to use. Iam trying to build a top-level hierarchy where the API Request is made in a class at top level and then the result is passed around to child components to be used as props and then in states.
The problem is that when i pass the result i get "Object Promise" in my child component. How do I access the data sent as props to child components?
As you can see in my App.js in my render() method that i created a component of the class API and pass the result from the fetchData() method as parameter to the component.
In my API.js class i used console.log to check the result but
the result i get from the logs are:
line 5: {dataObject: Promise}
line 10: undefined
App.js:
import API from './API';
class App extends Component {
componentDidMount(){
this.fetchData();
}
fetchData(){
const url = "https://randomuser.me/api/?results=50&nat=us,dk,fr,gb";
return fetch(url)
.then(response => response.json())
.then(parsedJSON => console.log(parsedJSON.results))
.catch(error => console.log(error));
}
render() {
return (
<div className="App">
<API dataObject={this.fetchData()}/>
</div>
);
}
}
export default App;
API.js
import React from 'react';
class API extends React.Component{
constructor(props){
console.log(props);
super(props);
this.state = {
dataObj:props.dataObject
};
console.log(this.state.dataObject)
}
render() {
return(
<p>""</p>
)
}
}
export default API;
Try changing App.js to this:
import API from './API';
class App extends Component {
componentDidMount(){
this.fetchData();
}
fetchData(){
const url = "https://randomuser.me/api/?results=50&nat=us,dk,fr,gb";
return fetch(url)
.then(response => response.json())
.then(parsedJSON => this.setState({results: parsedJSON.results}))
.catch(error => console.log(error));
}
render() {
return (
<div className="App">
<API dataObject={this.state.results}/>
</div>
);
}
}
export default App;
This makes sure you fetch the data in componentDidMount and it now uses state to store the data which then will be passed into your API component.
If anyone is looking for an answer using Hooks then this might help.
App.js
import API from './API';
function App(props) {
const [result, setResult] = React.useState({});
// similar to componentDidMount
React.useEffect(() => {
this.fetchData();
}, []);
fetchData() {
const url = "https://randomuser.me/api/?results=50&nat=us,dk,fr,gb";
fetch(url)
.then(response => setResult(response.json()))
.catch(error => console.log(error));
}
return (
<div className="App">
<API dataObject={result}/>
</div>
);
}
export default App;
API.js
import React from "react";
function API(props) {
const [result, setResult] = React.useState(props.dataObject);
React.useEffect(() => {
setResult(result);
}, [result]);
return <p>{result}</p>;
}
export default API;
Hope it helps! And let me know if anything is incorrect.
You should fetch data in componentDidMount and not in render. Fetching the data within render causes the API request to be repeated, every time the DOM is re-rendered by react.js.
After making the GET request to the API endpoint, first parse the data into a javascript object, then set the results to state using this.setState from within your component.
From there, you may pass the data held in state to child components as props in the render function.
For example:
const App = (props) =>
<ChildComponent />
class ChildComponent extends React.Component {
constructor(props){
super(props);
this.state = {
results: []
}
}
componentDidMount(){
fetch('/api/endpoint')
.then(res => res.json())
.then(results => this.setState({results})
}
render(){
return <GrandchildComponent {...this.state} />
}
}
const GrandchildComponent = (props) =>
<div>{props.results}</div>
I am building a simple movie catalogue using themoviedb API however I am facing an issue that I am unable to solve.
The issue is that the result after fetching is always undefined.
I tried with the method componentWillMount to fetching data and the setting the state inside this method but it does not work.
I tried to fetch inside constructor, no result.
This is my code so far
import React, { Component } from 'react';
import Header from './components/Header';
import MovieList from './components/MovieList';
import Footer from './components/Footer';
const MOVIE_API = "http://api.themoviedb.org/3/discover/movie?api_key=72049b7019c79f226fad8eec6e1ee889&language=en-US&sort_by=release_date.desc&include_adult=true&include_video=false&page=2&primary_release_year=2018";
//class
class App extends Component {
constructor(props){
super(props);
this.state = {
movies: [],
movieName: ''
}
}
componentWillMount(){
this.fetchMovie();
}
//fetching movie
fetchMovie = () =>{
const req = new Request(MOVIE_API, {
method: 'GET',
cache: 'default'
});
fetch(req).then(response =>{
return response.json();
}).then(data =>{
console.log(data); //REF 1;
this.setState({
movies: data
});
}).catch(err => {
console.log("ERROR: " + err);
})
}
render() {
return (
<div className="root">
<Header />
<MovieList moviesRes={this.state.movies}/>
<Footer />
</div>
);
}
}
export default App;
As you can see I called the method componentWillMount to fetch the data but it does not work.
It is also noticeable that if I log the data (REF 1) I can see the result (json).
===========================
EDIT
This is the code for MovieList
/*import React, { Component } from 'react';
export default class MovieList extends Component{
constructor(props){
super(props);
this.state = {
movies: this.props.movieRes
}
}
render(){
//if result is undefined
if(this.state.movieRes === undefined){
return(
<h1>Loading...</h1>
);
}else{
return(
<ul>
{this.state.movieRes.map((movie, index)=>{
return (
<li key={index}>{movie.title}</li>
);
})}
</ul>
);
}
}
}*/
=================
update child code
import React, { Component } from 'react';
export default class MovieList extends Component{
render(){
const { movieRes = [] } = this.props; // we are assigning a default prop here of an empty array.
return(
<ul>
{
//return movie from array
movieRes.map((movie, index)=>{
return (
<li key={index}>
{movie.id}
</li>
);
})
}
</ul>
);
}
}
In this I way I suppress the error, but still it is not working.
From what I learnt, React should render as soon as it detect changes but for some reason it not the case.
IMAGE
As you can see from the image when I am passing the array from parent component to the child component the array length is 20 but in the child component the array length seems to be 0
===================
Solution
I changed the component from class to a const and pass to it the array and everything went smooth. Here is the final code:
import React from 'react';
const MovieList = ({movies}) =>{
if(!movies){
return <h1>Loading...</h1>
}
return (
<ul>
{
movies.map((movie, index) => {
return (
<li key={index}>
<p>{movie.title}</p>
</li>
)
})
}
</ul>
);
}
export default MovieList;
Originally I misunderstood your issue but after re-reading it I noticed that you defined movies as an array in your constructor.
Without an actual error message, I'm going to assume that MovieList is expecting an array for it's prop movieRes and you're probably then trying to do something like .map or a loop to render the movies.
However, the API you're using doesn't return an array. It returns an object with an array key'd under results. So, I changed it to access data.results when doing setState.
//fetching movie
fetchMovie = () =>{
const req = new Request(MOVIE_API, {
method: 'GET',
cache: 'default'
});
fetch(req).then(response =>{
return response.json();
}).then(data =>{
console.log(data);
this.setState({
movies: data.results // <-- change made here.
});
}).catch(err => {
console.log("ERROR: " + err);
})
}
Here's a working JSFiddle:
https://jsfiddle.net/patrickgordon/69z2wepo/99513/
EDIT:
In the child component, instead of assigning props to state, just use props and default props.
import React, { Component } from 'react';
export default class MovieList extends Component{
render(){
const { movieRes = [] } = this.props; // we are assigning a default prop here of an empty array.
return(
<ul>
{movieRes.map((movie, index)=>{
return (
<li key={index}>{movie.title}</li>
);
})}
</ul>
);
}
}