Reputation: 23
Help, I cannot seem to figure out how to solve this problem:
array.map(object => {
console.log(this.state.newArr) // [] didn't update despite setState
if (this.state.newArr.length === 0) {
axios.get('http://localhost/')
.then(res => {
this.setState(prevState => ({ newArr: [...prevState.newArr, res] })) // setState here
})
}
})
Edit: updated code implemented the answers, but still doesn't work atm.
Upvotes: 0
Views: 68
Reputation: 4987
Async/await pattern can be a solution. Never tried it in a map but it would be something like this :
array.map(async object => {
if (this.state.newArr.length === 0) {
const response = await axios.get('http://localhost/');
await this.setStateAsync({ newArr: [...this.state.newArr, response] });
// or without the setStateAsync
// this.setState(prevState => ({ newArr: [...prevState.newArr, response] }));
}
});
const setStateAsync = state => {
return new Promise((resolve) => {
this.setState(state, resolve)
});
}
Upvotes: 1
Reputation: 14365
setState
is async, meaning when setting state in a loop you will not yet have the updated state values. When you make state updates based on previous state values, you should use the updater form of setState
.
class Example extends React.Component {
constructor() {
super();
this.state = {
arr: []
}
}
componentDidMount() {
[1,2,3].forEach((i) => {
// setState is async,
// so it has not updated by the next loop
// meaning its the same every cycle
console.log(this.state.arr)
this.setState({ arr: [...this.state.arr, i] });
})
}
render() {
// Only the last enty is in the array
console.log('final', this.state.arr)
return (<div>test</div>);
}
}
ReactDOM.render(<Example/>, document.getElementById('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>
<div id="root"></div>
class Example extends React.Component {
constructor() {
super();
this.state = {
arr: []
}
}
componentDidMount() {
[1,2,3].forEach((i) => {
// Use the updater form so you are guaranteed
// to have the most recent copy of state each update
this.setState((prevState) => ({ arr: [...prevState.arr, i] }));
})
}
render() {
console.log('final: ', this.state.arr)
return (<div>test</div>);
}
}
ReactDOM.render(<Example/>, document.getElementById('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>
<div id="root"></div>
Another feature of setState
that may come in handy here is the callback option.
You may pass a second argument to setState
that is a function. That function will be called after the state is updated, so you can add logic there that is dependent on those updated values.
class Example extends React.Component {
constructor() {
super();
this.state = {
arr: []
}
}
componentDidMount() {
[1,2,3].forEach((i) => {
this.setState((prevState) => ({ arr: [...prevState.arr, i] }), () => {
console.log('updated: ', this.state);
});
console.log('not updated: ', this.state);
})
}
render() {
return (<div>test</div>);
}
}
ReactDOM.render(<Example/>, document.getElementById('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>
<div id="root"></div>
Upvotes: 0