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!
Related
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
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.
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
I want to practice my reactjs skills so I am doing some exercises with api calls to the popular openweathermap api
I am making the api call in the componentDidMount() cycle which following the documentation here, is the better place to make the async call
If I console.log(data) in render I first get undefined then I get the object that I need; but if I try to access said object with {data.city.name} which does exist I get the error "TypeError: Cannot read property 'name' of undefined" on the second re-render (componentDidMount() forces a second render)
I do not know what I am missing, I am not that experienced with lifecycles but it is pretty straightforward, I do not understand why this is happening
Can anyone enlighten me please? Thank you.
import React from 'react';
class WeekForecast extends React.Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
data: {}
};
}
componentDidMount() {
fetch([url])
.then(res => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
data: result
});
},
(error) => {
this.setState({
isLoaded: true,
error
});
}
)
}
render() {
const { data } = this.state;
return(
<div>
<p>This weeks forecast in {data.city.name}</p>
</div>
)
}
}
export default WeekForecast;
You are seeing this error because you are trying to get a property from an object which is undefined. If you try to log a property which is undefined at that time is not a problem, but if you try to get a property from this undefined object then you get an error.
const data = {};
console.log( "city is", data.city );
console.log( "city name is", data.city.name );
Here is another example. What if we don't define an empty object for data? You are defining one in your state, but what if we don't?
//const data = {};
console.log( "city is", data.city );
console.log( "city name is", data.city.name );
Here since data is not defined, we can't get the city as undefined.
In your situation, data is defined in your state as an empty object. So, trying to log the city returns an undefined, but trying to log city.name returns error.
Because your data is landing in your component after the first render you should check you have it with a conditional rendering. You will use this all the time when you are dealing with the data which is coming from a remote place. There are many ways doing the conditional rendering. Here is one of them:
renderHelper() {
const { data } = this.state;
if ( !data.city ) { return <p>Loading data...</p>}
return <p>This weeks forecast in {data.city.name}</p>
}
render() {
return (
<div>
{ this.renderHelper() }
</div>
)
}
Hope the below snippet helps.
Couple of issues.
The Promise handle was misused.
Handling the data on the firs render (data validation before operating).
class WeekForecast extends React.Component {
constructor(props) {
super(props);
this.state = {
error: null,
isLoaded: false,
data: {}
};
}
fetch = () => Promise.resolve({city:{name:"New York City"}})
componentDidMount() {
this.fetch()
.then(
(result) => {
this.setState({
isLoaded: true,
data: result
});
},
(error) => {
this.setState({
isLoaded: true,
error
});
}
)
}
render() {
const { data } = this.state;
return(
<div>
<p>This weeks forecast in {data.city && data.city.name || 'No City'}</p>
</div>
)
}
}
ReactDOM.render(<WeekForecast />, document.querySelector("#app"))
<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='app'/>
I have a problem where in I'm going to access a state inside a method inside my parent component from my child component it returns me an undefined value which i'm sure in the first place have a value of objects in an array.
Parent Component:
class BoardList extends React.Component {
constructor(props){
super(props);
this.state = {
lists: []
};
}
componentWillMount(){
this.props.getBoardLists()
.then((result) => {
this.setState({
lists: result
});
})
.catch(error => {
console.log(error);
});
}
addBoardLists(result){
// This is i'm getting my undefine state lists :(
console.log(this.state.lists);
this.setState({
lists: this.state.lists.concat([result])
});
}
render() {
const { isLoading,data } = this.props;
if(isLoading){
return (
<Loading />
);
}
return (
<div className={style.boardListContainer}>
<h1 className={style.boardListTitle}>Personal Board</h1>
<Row>
<BoardItem item={this.state.lists} />
<BoardAdd onDisplay={this.fetchBoardLists} onAddItem={this.addBoardLists} />
</Row>
</div>
)
}
}
Child Component:
class BoardAdd extends React.Component {
constructor(props){
super(props);
this.state = {
name: '',
boardAddModalShow: false
}
}
openAddBoardModal(){
this.setState({ boardAddModalShow: true });
}
closeAddBoardModal(){
this.setState({ boardAddModalShow: false });
this.props.dispatch(reset('BoardAddModalForm'));
}
addBoard(formProps) {
this.props.addBoard(formProps).then((result) => {
// This is where I access my addOnItem from my parent component
this.props.onAddItem(result);
this.props.dispatch(reset('BoardAddModalForm'));
this.closeAddBoardModal();
})
.catch(error => {
console.log("error");
console.log(error);
});
}
}
Perhaps this will help?
class BoardList extends React.Component {
constructor(props){
super(props);
this.state = {
lists: []
};
this.addBoardList.bind(this)
}
What is this magical .bind? You should read up on what this means in JavaScript (which it almost never thinks what you think it means). By default, ES6 constructors do not bind (for some crazy reason in my opinion), their own methods to their own this value. Thus, the this in your method is referring to a completely different this you are thinking of and consequentially, making this scenario quite bizarre.
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/this