Want to get data in array from API in React Js - reactjs

I want to get data in array from the API but not able to find the exact solution and i am new to React.js
I am trying to get the data in temp array from the API but not able to figure it out how to use new state as i am already using one setState in componentDidMount method.
Code till componentDidMount method:
class Apiapp extends Component{
constructor(){
super()
this.state = {
loading:true,
characters:{}
}
}
componentDidMount(){
// This means we can use the setState method as many times are we can depending on what
type of methods are we using
// this.setState({
// loading:true
// })
fetch("https://pokeapi.co/api/v2/pokemon/5")
.then(response => response.json())
.then(data => {
let tmpArray = []
for (var i = 0; i < data.game_indices.length; i++) {
tmpArray.push(data.game_indices[i])
}
console.log(data)
this.setState({
loading:false,
characters:data
})
this.setState({
loading:false,
arrCharacters:tmpArray
})
})
}
Code of render method:
render() {
let text = this.state.loading ? <h2>Loading...</h2> : <div><h2>
{this.state.characters.name}
</h2>,<h2>{this.state.arrCharacters.name}</h2></div>
// <h2>{this.state.characters.game_indices[0].version.name}</h2>
return(<div>{text}</div>)
}
}
I am trying to get all the names that is in "game_indices".
API link: https://pokeapi.co/api/v2/pokemon/ditto

Don't overcomplicate things
import React, { Component } from 'react';
class ApiApp extends Component {
constructor(props) {
super(props);
this.state = {
loading: true,
characters: {},
};
}
async componentDidMount() {
const data = await fetch('https://pokeapi.co/api/v2/pokemon/5').then((response) => response.json());
this.setState({ characters: data.game_indices, loading: false });
}
render() {
return <div>{this.state.loading ? <h2>Loading...</h2> : this.state.characters.map((i) => i.version.name)}</div>;
}
}
I'm not too sure what data you are trying to display - its not that clear from the question

There are cleaner ways to achieve the same result. However, I'd rather explain what it is wrong with your code:
Check the comments below in your code
import { Component } from 'react'
class App extends Component {
constructor() {
super()
this.state = {
loading: true,
characters: {},
}
}
componentDidMount() {
this.setState({
loading: true,
})
fetch('https://pokeapi.co/api/v2/pokemon/5')
.then((response) => response.json())
.then((data) => {
let tmpArray = []
for (var i = 0; i < data.game_indices.length; i++) {
tmpArray.push(data.game_indices[i])
}
this.setState({
loading: false,
characters: data,
})
this.setState({
loading: false,
arrCharacters: tmpArray,
})
})
}
// Code of render method:
render() {
let text = this.state.loading ? (
<h2>Loading...</h2>
) : (
<div>
<h2>{this.state.characters.name}</h2>,
{/* 1 - When the page first loads "arrCharacters" is undefined.
Therefore you need to add a condition to make sure it is not undefined.
2- You need to loop through all elements to display the name for each of them.
For that, you can use the js array method map.
3- When you display a list, you must use a unique key as attribute.
4 - After you need to check where the data you want to display lives.
In your case, it is inside an obj version. So you access it with "." or "[]"
*/}
{this.state.arrCharacters &&
this.state.arrCharacters.map((char) => (
<h2 key={char.version.name}>{char.version.name}</h2>
))}
</div>
)
return <div>{text}</div>
}
}
export default App

Related

Cloning Dictionaries in React JSX

I have a hard time finding the answer to this question. Say I have the following script (which doesn't work):
class ProfileCard extends React.Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
items: {},
};
}
componentDidMount() {
fetch("/accounts/api/" + username)
.then(res => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
items: result
});
},
(error) => {
this.setState({
isLoaded: true,
error
});
}
)
}
}
This is what result and this.state.items look like, respectively:
{last_login: "2020-06-25T09:50:24.218Z", is_superuser: "true", is_staff: "true", …}
{}
How can I make the this.state.items have the exact same content as result? Please assume that all variables and methods used have been declared.
how are you?
How is your render()? Can you specify more your question?
For me, the way to put the results inside the items is right, but it only sets the state and changes the content of items after rendering.
Below an example:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
image: ''
}
this.urlImage = this.urlImage.bind(this);
}
urlImage() {
fetch("https://dog.ceo/api/breeds/image/random")
.then( (res) => res.json())
.then((results) => this.setState({ image: results }))
}
componentDidMount() {
this.urlImage()
}
render() {
const { image } = this.state;
if (image === '') return <h2>Loading...</h2>
return (
<div>
<h2>Dogs!</h2>
<div>
<img src={image.message} alt={'Dog'}/>
</div>
</div>
);
}
}
Sorry if you understand that I do not interpret your question correctly, but want to complement that render() was used as an example to show that this.setState() is asynchronous, so with render() you can use this change of state, or you could use componentDidUpdate() that is also executed in the state receives a new value.
componentDidUpdate () {
console.log (this.state.items)
}
https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous
In fact, you need to specify a question better, because a simple "my status is like this and I wish it could be like" this not described, I hope you can do what you want. Thanks!

Error: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application

I'm getting the above error and I don't know how to handle it.
I got a component. And in the render() i'm looping through an array and placing another component and parsing a value to that component like this:
render() {
let allProducts = this.state.products.map((product, i) => {
return (
<div key={product.article}>
...
<PriceStock value={product.article} />
...
</div>
)
})
}
In the PriceStock component i'm fetching some data with axios like the code below:
export default class PriceStock extends React.Component {
constructor(props) {
super(props);
this.state = ({
buttoprice: ''
})
this.getPriceAndStock = this.getPriceAndStock.bind(this)
}
getPriceAndStock(articleNo) {
return axios.post('LINK_TO_URL', {
articleNo: articleNo
}).then(result => {
return result.data
})
}
async componentDidMount() {
let pricestock;
pricestock = await this.getPriceAndStock(this.props.value)
let bruttoPrice = PRICE_TO_PARSE_TO_THE_STATE;
this.setState({ buttoprice: bruttoPrice })
}
render() {
return (
<div >
{this.state.buttoprice}
</div>
);
}
}
The error seems to happen when I try to setState in the componentDidMount, any suggestions?
this is an error occurs because you are updating state before it gets initialized
perform your loading activities in the constructor it is the right way to do it
getPriceAndStock(orderNumber, articleNo) {
return axios.post('LINK_TO_URL', {
orderNr: orderNumber, vareNr: articleNo
}).then(result => {
return result.data
})
}
constructor() {
this.getPriceAndStock(this.props.value)
.then(pricestock=>{
let bruttoPrice = PRICE_TO_PARSE_TO_THE_STATE;
this.state({ buttoprice: bruttoPrice })
})
.catch(console.log)
}
Found the answear in this question: https://github.com/material-components/material-components-web-react/issues/434
It's remindend me a little bit about the comment with another stackoverflow question.

How to fetch data from api and pass it as props

I'm in the learning phase of react and trying to figure out how to
fetch api data and pass it as props, so i created my own api file in
github and tried to fetch the api data from it, here is the link
below:
https://raw.githubusercontent.com/faizalsharn/jokes_api/master/jokesData.js
for some reason the data is not being fetched from the api and not
being passed as props could someone, please explain me where im doing
wrong, forgive me if there is any obvious mistakes here im still in
beginner level
App.js
import React, {Component} from "react"
import Joke from "./joke"
class App extends Component {
constructor() {
super()
this.state = {
loading: false,
jokeComponents: []
}
}
componentDidMount() {
this.setState({loading: true})
fetch("https://raw.githubusercontent.com/faizalsharn/jokes_api/master/jokesData.js")
.then(response => response.json())
.then(data => {
this.setState({
loading: false,
jokeComponents: data.jokesData.map(joke => <Joke key={joke.id} question={joke.question} punchLine={joke.punchLine} />)
})
})
}
render() {
const text = this.state.loading ? "loading..." : this.state.jokeComponents
return (
<div>
{text}
</div>
)
}
}
export default App
joke.js
import React from "react"
function Joke(props) {
return (
<div>
<h3 style={{display: !props.question && "none"}}>Question: {props.question}</h3>
<h3 style={{color: !props.question && "#888888"}}>Answer: {props.punchLine}</h3>
<hr/>
</div>
)
}
export default Joke
I check the API, and found out that it is not working properly when the response.json() is being invoke in the fetch API.
And this is due to the error in the response of the API. You just need to return a bare array, and not return the API with a variable.
For reference, please check the return json of the Jsonplaceholder Fake API. https://jsonplaceholder.typicode.com/posts
Hope this fix your error.
Also, for the state of the jokeComponents, please just have the array passed in the response, and not manipulate the data. Just use the .map for the jokeArray in the render() function if the state is changed. :)
To show content after it is being loaded and hide the loading indicator, use a function that simulates an async action and after that the data will be shown. I've shown this example with another API, as there is a problem with your API. I hope you fix that. Also set headers to allow cross domain data access.
App.js
import React, { Component } from "react";
import ReactDOM from "react-dom";
import Joke from "./Joke";
class App extends Component {
constructor() {
super();
this.state = {
loading: true,
jokeComponents: []
};
}
componentDidMount() {
fetch("https://jsonplaceholder.typicode.com/posts",{
headers: { crossDomain: true, "Content-Type": "application/json" }
}).then(response=>response.json())
.then(data => {
console.log(data);
this.setState({
jokeComponents: data.map(joke => (
<Joke
key={joke.id}
question={joke.title}
punchLine={joke.body}
/>
))
});
});
demoAsyncCall().then(() => this.setState({ loading: false }));
}
render() {
const { loading } = this.state;
if(loading) {
return "loading...";
}
return <div>{this.state.jokeComponents}</div>;
}
}
function demoAsyncCall() {
return new Promise((resolve) => setTimeout(() => resolve(), 2500));
}
ReactDOM.render(<App />, document.getElementById('root'));
The working code of the same is set up in CodeSandbox below:
Gideon Arces correctly explained your first bug, but there's more to do:
You need to format your .json file as json, which is not the same as javascript.
For example, while this is javascript {id: 1, question: "?"} it's not json. Json must be formatted like this: {"id": "1", "question":"?"} with quotes around the property names.
You need to do your data fetching in your componentDidMount and call setState there
You need to pull data from the state and render your components in render(). Typically this is done by creating an array of components and then putting them into the return inside {}. See more on that here: Lists and Keys
It's always a good idea to start with dummy data hardcoded into your component before you try to combine your ui with your api. See below in the componentDidMount() where I hardcoded some jokes. This way you can isolate bugs in your ui code from those in your network/api code.
class App extends React.Component {
constructor() {
super();
this.state = {
loading: false,
jokes: []
};
}
componentDidMount() {
// this.setState({loading: true})
// fetch("https://raw.githubusercontent.com/faizalsharn/jokes_api/master/jokesData.js")
// .then(response => response.json())
// .then(data => {
// console.log(data);
// this.setState({
// loading: false,
// jokes: data
// })
// })
const json = `[
{
"id": "1",
"question": "?",
"punchLine": "It’s hard to explain puns to kleptomaniacs because they always take things literally."
},
{
"id": "2",
"question": "What's the best thing about Switzerland?",
"punchLine": "I don't know, but the flag is a big plus!"
}
]`;
const jokes = JSON.parse(json);
this.setState({ jokes });
}
render() {
const jokeComponents = this.state.jokes.map(joke => (
<Joke key={joke.id} question={joke.question} punchLine={joke.punchLine} />
));
console.log(jokeComponents);
const text = this.state.loading ? "loading..." : jokeComponents;
return <div>Text: {text}</div>;
}
}
function Joke(props) {
console.log("joke props:", props);
return (
<div>
<h3 style={{ display: !props.question && "none" }}>
Question: {props.question}
</h3>
<h3 style={{ color: !props.question && "#888888" }}>
Answer: {props.punchLine}
</h3>
<hr />
</div>
);
}

ReactJS - Pass Updated Value To Sub-Component Method

I'm working on an environment that is basically set up with a Main Component like this:
class MainComponent extends Component {
constructor(props) {
super(props);
this.state = {
selectedValues: []
};
}
render() {
const { selectedValues } = this.state;
return (
// Other components
<SubComponent selectedValues = {selectedValues} />
// Other components
);
}
}
export default MainComponent;
And a Sub Component like this:
class SubComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
isExporting: false,
selectedValues: props.selectedValues
};
}
performTask = () => {
this.setState({ isWorking: true });
const { selectedValues } = this.state;
console.log(`Selected Values: ${selectedValues}`);
fetch('/api/work', {
method: 'GET'
})
.then(res => res.json())
.then((result) => {
// Handle the result
this.setState({ isWorking: false });
})
.catch((error) => {
console.log(error);
this.setState({ isWorking: false });
});
};
render() {
const { isWorking } = this.state;
return (
<Button
bsStyle="primary"
disabled={isWorking}
onClick={() => this.performTask()}
>
{isWorking ? 'Working...' : 'Work'}
</Button>
);
}
}
SubComponent.propTypes = {
selectedValues: PropTypes.arrayOf(PropTypes.string)
};
SubComponent.defaultProps = {
selectedValues: []
};
export default SubComponent;
In the Main Component, there are other components at work that can change the selectedValues. The functionality I'd like to see is that when the performTask method fires, it has the most recent and up to date list of selectedValues. With my current setup, selectedValues is always an empty list. No matter how many values actually get selected in the Main Component, the list never seems to change in the Sub Component.
Is there a simple way to do this?
I would suggest you 2 of the following methods to check this problem:
Maybe the state.selectedItems doesn't change at all. You only declare it in the contractor but the value remains, since you didn't setState with other value to it. Maybe it will work if you will refer to this.props.selectedItems instead.
Try to add the function component WillReceiveProps(newProps) to the sub component and check the value there.
If this method doesn't call, it means the selectedItems doesnt change.
Update if some of it works.
Good luck.
selectedValues in SubComponent state has not updated since it was set in SubComponent constructor. You may need to call setState again in componentWillReceivedProps in SubComponent

Cannot read property 'map' of undefined with REACTJS

I am new with reactjs.
This is what I am trying
class EventDemo extends Component {
constructor(){
super()
this.getStarWars()
this.state = {}
}
getStarWars = ()=> axios.get('https://swapi.co/api/people')
.then(res => {
console.log(res.data)
this.setState({
names: res.data.results
})
})
render() {
console.log(this.state.names);
return (
<div>
{this.state.names.map(function(e){
return <li>{e.name}</li>
})}
</div>
);
}
}
But This following error i am getting
What I am doing wrong here ? It supposed to work .
First of all,you shouldn't call your this.getStarWars() function inside the constructor, it is a very bad practice and could cause you troubles, http calls in React component should be generally called from the componentDidMount function.
However the issue in this case is another one,you haven't given an initial value to this.state.names, so when the component tries to do the initial render it fails because the names are undefined since the initial render appens before the http call is resolved
You code should be fixed like this:
class EventDemo extends Component {
constructor(){
super()
this.state = { names:[] }
}
componentDidMount(){
this.getStarWars()
}
getStarWars = ()=> axios.get('https://swapi.co/api/people')
.then(res => {
console.log(res.data)
this.setState({
names: res.data.results
})
})
render() {
console.log(this.state.names);
return (
<div>
{this.state.names.map(function(e){
return <li>{e.name}</li>
})}
</div>
);
}
}

Resources