how come my array is not being rendered on my react page? - javascript

Can someone help me out and tell me why it is not rendering my array object. The data is being fetched from my Mongoose database which gets printed on the console, but how come it is not rendering on the page.
Thanks!
class produce extends Component {
constructor() {
super();
this.state = {
loading: true,
fruits: []
};
}
componentDidMount() {
fetch('http://localhos:2000/api/fruits')
.then(response => response.json())
.then(produce => {
this.setState({ produce });
console.log(produce);
})
.catch(error => console.log(error));
}
render() {
return (
<div>
<ul>
this text above shows
{this.state.fruit.map(d => (
<li key={d.id}>{d.fruit}</li>
))}
</ul>
</div>
);
}
}

When you call the setState function with the parameter { produced }, you are creating the following state.
{
loading: true,
fruit: [],
produced: { data: [/* fruits data */] }
}
To avoid that, you need to access the property data in the API response and then assign it to the fruit property in your state.
The solution is changing the setStatecall to this.
this.setState({ fruit: produce.data })

Related

Catch error films.map is not a function in React

I get the catch error or type error on
{films.map((film, i) => { ...
can not read proper 'map' or undefined from the Rest Api swapi in React.
build id url to show information
Create the component
State to add api data
Binding the function fetchData to this state
Call to api function characters
Call to api function movies
Life circle to call the function
Rendering the component
Bring the data of the character to the data to show them
mapping the list
const api_url_films = 'https://swapi.dev/api/films/'
class Card extends Component {
constructor(props) {
super(props)
this.state = {
films: []
}
this.fetchDataFilms = this.fetchDataFilms.bind(this)
}
fetchDataFilms(){
fetch(api_url_films)
.then(data => (data.json()))
.then(results => {
const api_data = results
this.setState({
films: api_data
})
})
}
componentDidMount() {
this.fetchDataFilms()
}
render() {
const {films} = this.state
return(
<div>
{films.map((film, i) => {
return (
<li key={i}>{film}</li>
)
}
)}
</div>
)
}
}
I try to add in a unordered list also but doesn't work
thanks
I think html is rendered before data is fetched although the films variable is an empty array in initial state.
When films variable is set from fetching function, html will be re-rendered.
try this code. This component will show an empty box until film data is fetched.
hope this works!
render() {
let films = this.state.films
if(films == null || films == []) return <></>
return(
<div>
{films.map((film, i) => {
return (
<li key={i}>{film}</li>
)
}
)}
</div>
)
}
The response has this shape:
{
count: number,
next: any,
previous: any,
results: Object[], // <-- this is what you want
}
Update the handler to access into the response object and save the results array.
fetchDataFilms(){
fetch(api_url_films)
.then(data => (data.json()))
.then(results => {
const api_data = results
this.setState({
films: api_data.results, // <-- access the results array
})
})
}
Additionally, the film objects you are mapping are still object, so they are not valid React children. You will need to access specific properties.
{films.map((film) => {
return (
<li key={film.title}>{film.title}</li> // or director, or producer, etc...
)
}

Render() state element is initially empty until api call

I have a class component which should display some list values after an API call, in my render function I call a function to populate some other state list (with specific object properties from the fetched list), the problem is that in the render call, the state values are initially empty and as such the component I return is also just empty.
I've tried using componentDidUpdate() but I dont have much of an idea on how to go about using it, it usually gives me an infinite loop.
Here is my relevant code:
class AdminSales extends React.Component {
constructor(props) {
super(props);
this.state = {
items: [],
data: [],
};
}
componentDidMount() {
this.fetchData();
}
fetchData() {
fetch("/api/items")
.then((res) => res.json())
.then((items) => this.setState({ items: items }));
}
componentDidUpdate(prevState) {
if (JSON.stringify(prevState.items) == JSON.stringify(this.state.items)) {
// Do nothing
} else {
// This gives infinite loop ...
// this.fetchData();
}
}
populateData() {
this.state.items.forEach(function (item) {
this.state.data.push({
name: item.name,
value: item.quantity,
});
}, this);
}
render() {
// Output shown line: 65
console.log(this.state);
this.populateData();
const { data } = this.state;
return ( ... );
}
}
export default AdminSales;
Any help will be much appreciated.
First of all there are multiple issues in your code
Updating/Mutating state in render
Instead of updating the state using setState, updating the state inplace in populateData method
Also, as #Drew mentioned we don't have to duplicate items into data instead we can store only the required info in the state once after getting the response from the API.
In the meantime while waiting for the response if you want to show a loading info you can do that as well.
Below is the example covering all those points mentioned above.
const mockAPI = () => {
return new Promise((resolve) => setTimeout(() => {
resolve([{id:1, name: "ABC", quantity: 1}, {id: 2, name: "DEF", quantity: 5}, {id: 3, name: "XYZ", quantity: 9}])
}, 500));
}
class AdminSales extends React.Component {
constructor(props) {
super(props);
this.state = {
items: [],
loading: true
};
}
componentDidMount() {
this.fetchData();
}
fetchData = () => {
mockAPI()
.then((res) => {
this.populateData(res);
});
}
populateData = (data) => {
this.setState({
items: data.map(({name, quantity}) => ({
name,
value: quantity
})),
loading: false
})
}
render() {
//console.log(this.state);
const { items, loading } = this.state;
return loading ? <p>Loading...</p> :
items.map(item => (
<div>{item.name}: {item.value}</div>
));
}
}
ReactDOM.render(<AdminSales />, document.getElementById("react"));
<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>
<div id="react"></div>
Note: For simplicity I've mocked the backend API with simple setTimeout.

Cannot call the first element of a JSON object

I'm trying to access the first object from data[]. Then, grab the keys using Object.keys() but it gives me this error:
"TypeError: Cannot convert undefined or null to object".
I need the output to be an array of the keys.
import React, { Component } from 'react';
class CodecChart extends Component {
constructor(props) {
super(props);
this.state = {
post: [],
isLoaded: false,
}
}
componentDidMount() {
const url = 'https://jsonplaceholder.typicode.com/users';
fetch(url)
.then(result => result.json())
.then(post => {this.setState({ post: post })
})
}
render() {
const data = this.state.post;
// cannot reach the first object of data[]
var keys = Object.keys(data[0]);
return (
<div>
//output should be an array of the keys
<h5>{keys}</h5>
</div>
)
}
}
export default CodecChart;
The first time you try to access data[0], it's still empty:
this.state = {
post: [],
isLoaded: false,
}
and const data = this.state.post; means that data[0] is undefined.
it's only after the component is mounted, and the state is set correctly that data[0] is defined (or not, depending on what the API returns).
I found a way for it to work by adding another "then" so it can set the "keys" state right after the "posts" state was set. But I wonder if there is another way to make it more elegant. Thank you for trying to help.
constructor(props) {
super(props);
this.state = {
posts: [],
isLoaded: false,
keys: []
}
}
componentDidMount() {
const url = 'https://jsonplaceholder.typicode.com/users';
fetch(url)
.then(result => result.json())
.then(posts => {
this.setState({ posts: posts })
})
.then(_ => { this.setState({ keys: Object.keys(this.state.posts[0]) }) })
}
render() {
const keys = this.state.keys;
return (
<div>
<h5>{keys}</h5>
</div>
)
}

Console.log() shows undefined before getting data

I seem to have a lifecycle hook issue that I can't seem to solve.
export default class EditRevision extends Component {
state = {
data: [],
customColumns: []
}
componentWillMount = () => {
axios.get('http://localhost:8080/lagbevakning/revision/subscriptions?id=' + (this.props.match.params.id)).then(response => {
this.setState({
data: response.data,
loading: false
})
})
}
render() {
/* THIS IS THE CONSOLE.LOG() I AM REFERRING TO */
console.log(this.state.data.subscriptionRevisionDTOS)
return (
<div></div>
)
}
}
And this is my log upon rendering the component
https://i.gyazo.com/9dcf4d13b96cdd2c3527e36224df0004.png
It is undefined, then retrieves the data as i desire it to, then it gets undefined again.
Any suggestions on what causes this issue is much appreciated, thank you.
Replace this:
componentWillMount = () => {
axios.get('http://localhost:8080/lagbevakning/revision/subscriptions?id=' + (this.props.match.params.id)).then(response => {
this.setState({
data: response.data,
loading: false
})
})
with:
constructor(props){
super(props)
this.state = {
data: [],
customColumns: []
}
axios.get('http://localhost:8080/lagbevakning/revision/subscriptions?id=' + (this.props.match.params.id)).then(response => {
this.setState({
data: response.data,
loading: false
})
})
}
try to call axios in constructor or componentDidMount() (componentWillMount should not be used). the undefined result is caused by the async call. Looks like you have a lot of uncontrolled renders. try to add a shouldComponentUpdate function or convert your component in a PureComponent
Take a look at https://reactjs.org/docs/react-component.html
You have init the state with
state = {
data: [],
customColumns: []
}
Here this.state.data is empty array which did not have definition of
subscriptionRevisionDTOS that is why you are getting this.state.data.subscriptionRevisionDTOS undefined.
Meanwhile, your asyncaxios.get call is completed and this.state.data is updated with subscriptionRevisionDTOS.
As soon as state is updated render() called again and you are getting the proper value of this.state.data.subscriptionRevisionDTOS.
So below line will surely work.
state = {
data:{subscriptionRevisionDTOS:[]},
customColumns: []
}
export default class EditRevision extends Component {
state = {
data:{subscriptionRevisionDTOS:[]},
customColumns: []
}
componentDidMount = () => {
axios.get('http://localhost:8080/lagbevakning/revision/subscriptions?id=' +
(this.props.match.params.id)).then(response => {
this.setState({
data: response.data,
loading: false
})
})
render() {
/* THIS IS THE CONSOLE.LOG() I AM REFERRING TO */
console.log(this.state.data.subscriptionRevisionDTOS)
return (
<div></div>
)
}
see this it should be like this

React Native state isn't changing

I'm making a Ajax request to a Json file that return some movies.
state = { movies: [] };
componentWillMount()
{
this.getMovies();
}
/*
Make an ajax call and put the results in the movies array
*/
getMovies()
{
axios.get('https://pastebin.com/raw/FF6Vec6B')
.then(response => this.setState({ movies: response.data }));
}
/*
Render every movie as a button
*/
renderMovies()
{
const { navigate } = this.props.navigation;
return this.state.movies.map(movie =>
<ListItem key={ movie.title }
title={ movie.title }
icon={{ name: 'home' }}
onPress={() =>
navigate('Details', { title: movie.title, release: movie.releaseYear })
}
/>
);
}
render() {
return(
<List>
{ this.renderMovies() }
</List>
);
}
The error I get is the following: this.state.map is not a function. This is because movies is still empty.
When I console.log response.data it returns all the rows from the JSON file. So the problem is most likely in this line:
.then(response => this.setState({ movies: response.data }));
Does someone know what's wrong?
You put initial state in the wrong place. Do this instead:
constructor(props) {
super(props);
this.state = { movies: [] };
}
From document:
In general, you should initialize state in the constructor, and then
call setState when you want to change it.
Update you ajax request as following:
/*
Make an ajax call and put the results in the movies array
*/
getMovies()
{
let self = this;
axios.get('https://pastebin.com/raw/FF6Vec6B')
.then(response => self.setState({ movies: response.data }));
}
Also, you can bind your function inside constructor as:
constructor(props){
super(props);
this.getMovies = this.getMovies.bind(this);
}

Categories

Resources