React - Empty Array When Rendering JSON Data - javascript

Why is an empty array created when rendering this JSON array? See attached screenshot I assume the constructor is just initiating it with a null value and filling it at a later point.
New to Javascript + React and just want to make sure I am understanding what is happening. I will also accept critique on the garbage code that is below. Codepen link
class Jobs extends React.Component {
render() {
const jobs = this.props.jobs;
console.log(jobs);
const formattedJobs = jobs.map((job) =>
<ul key={job.id}>
<div class="company">{job.company_name}</div>
<div class="title">{job.title}</div>
</ul>
);
return(
<div>{formattedJobs}</div>
);
}
}
class App extends React.Component {
constructor() {
super();
this.state={
jobs:[]
}
var myUrl = "https://codepen.io/jobs.json";
fetch(myUrl)
.then((response) => response.json())
.then((json) => this.setState({jobs: json.jobs}));
}
render() {
return (
<div className="app">
<div className="header">
<h1 id="header-title">Job Postings</h1>
</div>
<div className="content">
<Jobs jobs={this.state.jobs}/>
</div>
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
);

You are getting the jobs from an ajax request wich is async. Thats why the initial value is an empty array.
App.render is executed before your ajax request is finished and thats why you are not givin any job to the Jobs component. Once the ajax is finished, the jobs array is filled with the results and sendeded to the Jobs component to render the results of that ajax request.

always use fetch statements in ComponentDidMount as it is called just after your component is rendered for the very first time
ComponentDidMount {
fetch(myUrl)
.then((response) => response.json())
.then((json) => this.setState({jobs: json.jobs}));
}

Jobs component
always be nice for default props - component can / will render before fetch returns response
replace class with className as first one is restricted word in JSX
ul can only contains li childs - not div
nice for use key property when you iterate over collection
App component
there is place for suppor unexpected response, you can set new state like error and support it
please consider with component is responsible to support errors and spinners- App or Jobs
Ract application
class Jobs extends React.Component {
render() {
const { jobs } = this.props;
const formattedJobs = jobs.map(job =>
<ul key={job.id}>
<li className="company">{job.company_name}</li>
<li className="title">{job.title}</li>
</ul>
);
return <div>{formattedJobs}</div>;
}
}
Jobs.defaultProps = {
jobs: []
};
class App extends React.Component {
constructor() {
super();
this.state = {
jobs: []
};
}
componentDidMount() {
fetch("https://codepen.io/jobs.json")
.then(response => response.json())
.then(response => {
if (!Array.isArray(response.jobs)) {
throw new Error(
`Expected response but got ${JSON.stringify(response.jobs)}`
);
} else {
return response.jobs;
}
})
.then(jobs => this.setState({ jobs }));
}
render() {
return (
<div className="app">
<div className="header">
<h1 id="header-title">Job Postings</h1>
</div>
<div className="content">
<Jobs jobs={this.state.jobs} />
</div>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));

Related

I keep getting this error Objects are not valid as a React child {} If you meant to render a collection of children, use an array instead

This is currently how my code looks
import React from 'react';
import './App.css';
class App extends React.Component {
state = {
apiData: []
}
render() {
console.log('api data is')
return (
<div>
<center>
<h1 id="title">hello something</h1></center>
<h1 id="date">{this.state.apiData.title}</h1>
</div>
)
}
componentDidMount() {
fetch('http://www.mocky.io/v2/5dece3d333000052002b9037')
.then(response => response.json())
.then(data => {
this.setState({
apiData: data
})
})
console.log("component fetched data")
}
}
export default App
I get this error when I try access something that has a value but when I do this
<h1 id="date">{this.state.apiData.date}</h1>
It works
not too sure how to fix as everything I have seen thus far is for data they have created through a const or let as opposed to fetching data from an API
apiData.title is of type Object, not an Array of JSX children or strings.
You need to use apiData.title.rendered, as shown in this snippet of response data from the API:
"title": {
"rendered": "Whatsapp and Facebook Messenger: how group chat is changing the dynamic of our friendships"
},
Try with a null check {this.state.apiData.title?.rendered}
Update: Try to open the link you're fetching the data from and follow the paths inside, for hero_image you need {this.state.apiData.acf?.hero_image.url}
As render fire before state set so you need to check before state update and use condition or && on "this.state.apiData.title" like this
class App extends React.Component {
state = {
apiData: []
}
render() {
console.log('api data is')
return (
<div>
<center>
<h1 id="title">hello something</h1></center>
<h1 id="date">{this.state.apiData.title && this.state.apiData.title.rendered}</h1>
</div>
)
}
componentDidMount() {
fetch('http://www.mocky.io/v2/5dece3d333000052002b9037')
.then(response => response.json())
.then(data => {
this.setState({
apiData: data
})
})
console.log("component fetched data")
}
}
ReactDOM.render(
<App />
, document.getElementById('root'));
<div id="root" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

Can't call class functions from main, "is not a function" error

I'm attempting to learn react by creating a simple weather app which uses api requests to get the data. The api requires a location key which can be obtained through another function. this means i have to return and then pass the key along to the next function but I seem to be having some trouble doing this!
TypeError: _LocationKey__WEBPACK_IMPORTED_MODULE_3__.default.setLocation is not a function" is the error i am given when I attempt to run.
I'm assuming I made an error with how i am calling the functions as well as how the actual classes are structured. Any help would be appreciated!
//The main app
function App() {
//runs function to set api location key
new LocationKey();
LocationKey.setLocation('winnipeg');
//uses get location key to set weatherData
new Weather();
Weather.getWeatherData(LocationKey.getLocation());
//displays location and weather information
return (
<div className="body">
<LocationKey/>
<Weather/>
</div>
);
}
//The location key class
export class LocationKey extends Component{
state = {
locations: []
}
setLocation(name) {
axios.get('http://dataservice.accuweather.com/locations/v1/cities/search?apikey=oqAor7Al7Fkcj7AudulUkk5WGoySmEu7&q=' + name + '&language=en&details=false')
.then(res => {
const locations = res.data;
this.setState({ locations });
})
}
getLocation(){
return this.state.locations[0].Key;
}
render() {
return (
<ul>
{ this.state.locations.slice(0, 1).map(location => <li>{location.EnglishName}</li>)}
</ul>
)
}
}
export default LocationKey
//Weather Class
export class Weather extends Component{
state = {
weatherData: []
}
//issues command to find weather data from api using location key
getWeatherData(location) {
axios.get('http://dataservice.accuweather.com/forecasts/v1/daily/1day/' + location + '?apikey=oqAor7Al7Fkcj7AudulUkk5WGoySmEu7&language=en&details=false&metric=true%20HTTP/1.1')
.then(res => {
const weatherData = res.data;
this.setState({ weatherData });
})
}
render() {
return (
<ul>
{ this.state.weatherData.slice(0, 2).map(weather => <li>{weather.Headline.Text}</li>)}
{ this.state.weatherData.slice(0, 2).map(weather => <li>{weather.Headline.Category}</li>)}
{ this.state.weatherData.slice(0, 2).map(weather => <li>{weather.DailyForecasts.Maximum.Value}</li>)}
{ this.state.weatherData.slice(0, 2).map(weather => <li>{weather.DailyForecasts.Minimum.Value}</li>)}
</ul>
)
}
}
export default Weather
The problem
The issue lies with this line:
LocationKey.setLocation('winnipeg');
You instantiate a new instance of LocationKey, but seeing as LocationKey isn't a singleton and setLocation is not a static method, you're trying to call an instance method in a static way.
Another potential issues is this line:
Weather.getWeatherData(LocationKey.getLocation());
As I said above, you instantiate a new instance of Weather, but since it isn't doing anything and isn't assigned to anything, it gets immediately thrown away, so the following line has no effect on it.
The solution
I would suggest making App its own component and having it contain the state for the locations, then passing the locations array to each of the components.
class App extends React.Component {
state = {
locations: ['winnipeg'],
locationInput: ''
};
render() {
return (
<div>
<input value={this.state.locationInput} onChange={e => this.setState({ locationInput: e.target.value })} />
<button onClick={this.handleAddLocation}>add to locations</button>
<Weather locations={this.state.locations} />
<LocationKey locations={this.state.locations} />
</div>
)
}
handleAddLocation = () => {
if (this.state.locationInput === '') return;
this.setState({
locationInput: '',
locations: [...this.state.locations, this.state.locationInput]
});
};
}
class Weather extends React.Component {
static defaultProps = {
locations: []
}
render () {
return (
<div>
<h2>Weather Component</h2>
<h3>Locations provided:</h3>
<span>{this.props.locations.join(', ')}</span>
</div>
);
}
}
class LocationKey extends React.Component {
static defaultProps = {
locations: []
}
render () {
return (
<div>
<h2>LocationKey Component</h2>
<h3>Locations provided:</h3>
<span>{this.props.locations.join(', ')}</span>
</div>
);
}
}
ReactDOM.render( < App / > ,
document.getElementById('app')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<div id="app"></div>
In the above snippet, I set out to show you how props can be used and provided to child components.
I hope this can help and clarify some of this issues you've been having

Modifying State of Component - Error: Objects are not valid as a React child

I currently trying to get myself familiar with react and the state of a component.
I am trying to create a component, use the constructor to initialise the state, do a get request to the server to get an array of object and finally I am trying to update the state of the component accordingly. But I end up with the following error and I can't understand the problem:
Objects are not valid as a React child (found: object with keys {title, content}).
My code looks like this:
import React, { Component } from 'react';
import axios from "axios";
class Display extends Component {
constructor(){
super();
this.state = { posts: []}
}
componentDidMount(){
this.fetchData();
}
fetchData(){
const ROOT_URL = "http://localhost:5000";
axios.get(`${ROOT_URL}/api/post`)
.then(response => response.data.map(post => (
{
title: post.title,
content: post.content
}))
)
.then(
posts => {
this.setState({
posts
})
}
)
}
render(){
const { posts } = this.state;
return (
<div className="article">
List of Posts:
{this.state.posts}
</div>
)
}
}
export default Display;
Does anyone know what am I doing wrong?
Thanks
this.state.posts is an object like
{
title: post.title,
content: post.content
}
and hence you can't render it direct like
<div className="article">
List of Posts:
{this.state.posts}
</div>
You might as well render it like
<div className="article">
List of Posts:
{this.state.posts.map((post) => {
return <div>
<div>{post.title}</div>
<p>{post.content}</p>
</div>
})
</div>
above is assuming that content is also not an object or array. Check How to render an array of objects for more details
You would need to iterate over the posts array and return the html you want to display
Something like this
List of Posts:
{this.state.posts.map(post=>(
<div>
<span>{post.title}</span>
<p>{post.content}</p>
</div>
))}

How do I iterate over a result returned by 'fetch' in javascript?

The following react application should fetch data from a RESTful API when a button is clicked and represent the data as a ul:
import React from 'react';
import ReactDOM from 'react-dom';
class RestSample extends React.Component{
constructor(props) {
super(props);
this.state = { items: [] };
}
fetchHotels(event){
fetch('http://localhost:8000/ui/rest/hotel')
.then(data => {
this.setState({items:data.json()});
});
}
render(){
return(
<div>
<button onClick={(event)=>this.fetchHotels(event)}>Hotel list</button>
<ul>
{this.state.items.map(function(item){
return(
<li></li>
)
})}
</ul>
</div>
)
}
}
ReactDOM.render(
<RestSample />,
document.getElementById('root')
);
However, on click I get a 'this.state.items.map is not a function' error because this.state.items is a promise and not a list when render is called.
Where do I have to place the code to iterate over this.state.items to make sure that it is called once the promise has returned a the list?
Try
fetchHotels(event){
fetch('http://localhost:8000/ui/rest/hotel')
.then(response => response.json())
.then(json=> {
this.setState({items:json.data.map(child => child)});
});
}
Please see https://redux.js.org/docs/advanced/AsyncActions.html for good example .
You should not use map initially , give any condition before using loop or map whether data is available in your variable , if it is then only loop else do some default thing.
Example i have given check once:
render(){
return(
<div>
<button onClick={(event)=>this.fetchHotels(event)}>Hotel list</button>
<ul>
{(this.state.items)?this.state.items.map(function(item){
return(
<li></li>
)
}):null}
</ul>
</div>
)
}

How to fetch API data and show that on table form in react.js

Here is my code I'm not getting how to show data on the page. When I tried I got nothing on the page and it shows:
Warning: Each child in an array or iterator should have a unique "key"
prop.
Here is my Code.
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(){
super();
this.state = {
data: []
}
}
componentDidMount()
{
fetch("https://blockchain.info/ticker").
then((Response) => Response.json()).
then ((findresponse)=>
{
console.log(findresponse)
this.setState({
data: [findresponse]
});
})
}
render()
{
return(
<div>
{
this.state.data.map((dynamicData, Key) =>
<div>
<span>{dynamicData.key}</span>
</div>
)
}
</div>
)
}
}
export default App;
This is a react warning.
As per the docs:
A “key” is a special string attribute you need to include when
creating lists of elements.
You are having an issue accessing the data returned from bitcoin as you have an array of objects. To access the data being returned from the API you need to do something like the following:
class Example extends React.Component {
constructor() {
super();
this.state = {
data: []
}
}
componentDidMount() {
fetch("https://blockchain.info/ticker").
then(response => response.json()).
then(findresponse => {
this.setState({
data: [findresponse]
});
})
}
render() {
return (
<div>
{
this.state.data.map((dynamicData, Key) => {
let keys = Object.keys(dynamicData);
let d = dynamicData;
return keys.map(data => {
return (
<div style={{borderBottom: '1px solid black'}}>
<p>Currency: {data}</p>
<p>Buy: {dynamicData[data].buy}</p>
</div>
);
});
})
}
</div>
)
}
}
ReactDOM.render(<Example />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Is there anything wrong at all with your code or is it just a question about the warning message? If that's the case, whenever you map through an array in React, you have to give them a key prop. This is helpful so React knows which items have changed, updated or even added in the future.
// Key here stands for dynamicData's index in the array and not its key prop.
this.state.data.map((dynamicData, Key) =>
<div key={Key}>
<span>{dynamicData.key}</span>
</div>
)
I suggest you to rename it from Key to index so you don't confuse keys within your code.
this.state.data.map((dynamicData, index) =>
<div key={index}>
<span>{dynamicData.key}</span>
</div>
)
For more information about keys in React, I suggest you to read this link.

Categories

Resources