I learning design ui with react library. But I don't know what happended in my code.
handleIncrement = () => {
this.setState({
...this.state,
quantity: this.state.quantity + 1
});
console.log(this.state);
document.getElementsByClassName("Counter-Order")[0].innerHTML++;
};
handleDecrement = () => {
this.setState({
...this.state,
quantity: this.state.quantity > 1 ? this.state.quantity - 1 : 1
});
if (this.state.quantity > 1) {
document.getElementsByClassName("Counter-Order")[0].innerHTML--;
}
};
Result image
Please have a look I hope it's helpful
Thanks
handleIncrement = () => {
this.setState({
...this.state,
quantity: this.state.quantity + 1
},()=>{
console.log(this.state);
document.getElementsByClassName("Counter-Order")[0].innerHTML++;
});
};
handleDecrement = () => {
this.setState({
...this.state,
quantity: this.state.quantity > 1 ? this.state.quantity - 1 : 1
},()=>{
if (this.state.quantity > 1) {
document.getElementsByClassName("Counter-Order")[0].innerHTML--;
}
});
};
This is the intended behaviour. The set state updates the state after the function has executed since it's an asynchronous function.
So actually what happens is.
The setState is called.
Console log happens.
State updates.
So to check the state after setState use the 2nd parameter (callback) in the set state.
this.setState(
{ ...this.state,
quantity: this.state.quantity + 1 },
() => console.log(this.state
);
You are violating the main idea of React by directly accessing DOM like document.getElementsByClassName("Counter-Order")[0].innerHTML++;
Ideally You should have something like this in your render function.
render() {
return <div className="Counter-Order">{this.state.quantity}</div>;
}
setState is an asynchronous function.
React internally waits for the best moment to change the state and re-render the component.
So the new state value isn't available immediatly after executing setState. That's why in your case your console.log still shows the previous state value.
Related
Can someone tell me what's wrong with my code? I am not able to get event.target.name even though I am assigning value to name property
handleSectionDObservation = event => {
if (event.target.value === "Observation" && event.target.name !== "Observation") {
this.setState({
...this.state,
CalcNoOfObs2: this.state.CalcNoOfObs2 + 1,
[event.target.name]: event.target.value
});
console.log([event.target.name])
} else if(event.target.name === "Observation") {
this.setState({
...this.state,
CalcNoOfObs2: this.state.CalcNoOfObs2 > 0 ? this.state.CalcNoOfObs2 - 1 : 0,
[event.target.name]: event.target.value
});
console.log([event.target.name])
};
}
The setState function will not update your values synchronously, your state will not be changed right away. The second parameter of it allows you to give a callback function triggering when the state has been mutated.
You also do not need to update all the state values at once, the one that you give are the only one that are going to be affected. And if you need to use the old state values, you should use the callback version of setState :
this.setState(oldState => ({
CalcNoOfObs2: oldState.CalcNoOfObs2 > 0 ? oldState.CalcNoOfObs2 - 1 : 0,
[event.target.name]: event.target.value
}), () => { console.log(this.state[event.target.name]) });
The whole function can also be reduced to the following to avoid repetitions :
handleSectionDObservation = event => {
if(event.target.name !== "Observation" && event.target.value !== "Observation") return;
const CalcNoOfObs2 = event.target.name === "Observation" ?
this.state.CalcNoOfObs2 > 0 ? this.state.CalcNoOfObs2 - 1 : 0 :
this.state.CalcNoOfObs2 + 1;
this.setState(oldState => ({
CalcNoOfObs2,
[event.target.name]: event.target.value
}), () => { console.log(this.state[event.target.name]) });
console.log([event.target.name]);
}
I'm trying to change one value inside a nested state.
I have a state called toDoItems that is filled with data with componentDidMount
The issue is that changing the values work and I can check that with a console.log but when I go to setState and then console.log the values again it doesn't seem like anything has changed?
This is all of the code right now
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
toDoItems: null,
currentView: "AllGroup"
};
}
componentDidMount = () => {
fetch("/data.json")
.then(items => items.json())
.then(data => {
this.setState({
toDoItems: [...data],
});
})
};
changeToDoItemValue = (givenID, givenKey, givenValue) => {
console.log(this.state.toDoItems);
let newToDoItems = [...this.state.toDoItems];
let newToDoItem = { ...newToDoItems[givenID - 1] };
newToDoItem.completedAt = givenValue;
newToDoItems[givenID - 1] = newToDoItem;
console.log(newToDoItems);
this.setState({
toDoItems: {newToDoItems},
})
console.log(this.state.toDoItems);
};
render() {
if (this.state.toDoItems) {
// console.log(this.state.toDoItems[5 - 1]);
return (
<div>
{
this.state.currentView === "AllGroup" ?
<AllGroupView changeToDoItemValue={this.changeToDoItemValue}/> :
<SpecificGroupView />
}
</div>
)
}
return (null)
};
}
class AllGroupView extends Component {
render() {
return (
<div>
<h1 onClick={() => this.props.changeToDoItemValue(1 , "123", "NOW")}>Things To Do</h1>
<ul className="custom-bullet arrow">
</ul>
</div>
)
}
}
So with my console.log I can see this happening
console.log(this.state.toDoItems);
and then with console.log(newToDoItems)
and then again with console.log(this.state.toDoitems) after setState
State update in React is asynchronous, so you should not expect updated values in the next statement itself. Instead you can try something like(logging updated state in setState callback):
this.setState({
toDoItems: {newToDoItems},// also i doubt this statement as well, shouldn't it be like: toDoItems: newToDoItems ?
},()=>{
//callback from state update
console.log(this.state.toDoItems);
})
This question already has an answer here:
Why doesn't my arrow function return a value?
(1 answer)
Closed 3 years ago.
Following is my code in which I am trying to increment the count using click button but it's not updating the value. Though I am not getting any error in console as well. Let me know what I am doing wrong here.
JS Code -
class App1 extends React.Component {
constructor(props) {
super(props)
this.state = {
count: 0
}
this.setCount = this.setCount.bind(this)
}
setCount() {
this.setState((state) => {
count: state.count + 1
})
}
render() {
return (
<>
<hr />
<h3>test increment</h3>
<button onClick={this.setCount}>Click</button>
<p>{this.state.count}</p>
</>
)
}
}
ReactDOM.render(<App1 />, document.getElementById('root'))
Codepen - https://codepen.io/anon/pen/LaMOEp
You are not returning anything. You could use return in side callback.
setCount() {
this.setState((state) => {
return {count: state.count + 1}
}))
}
Or you can use avoid using of return wrapping you return value in () after =>
setCount() {
this.setState((state) => ({
count: state.count + 1
}))
}
this.setState((state) => {
count: state.count + 1
})
In the above code, the curly brackets are the body of the function, count: is a line label, and state.count + 1 is an expression that never gets used. If you want to use the concise arrow function syntax to return an object literal, then you need to wrap the object in parentheses:
this.setState((state) => ({
count: state.count + 1
}))
The problem is in setCount(), where you miss a pair of parenthesis! Here's the correct version:
setCount() {
this.setState((state) => ({
count: state.count + 1
}));
}
There are two parenthesis more! One right after the => and one at then of the this.setState() call.
I'm fetching data (pics and descriptions) from server, and using them in infinite scroll. When I'm scroll to bottom of page I want to change state value (value of the page from which we collect data).
When I'm doing this, something goes wrong. State update correctly only on second and each next time.
Code:
class Gallery extends Component {
state = {
photos: [],
searchValue: "dog",
page: 1,
totalPages: null
};
getPhotos = () => {
const { photos, searchValue, page, totalPages } = this.state;
axios.get(`api_key=${API_KEY}&tags=${searchValue}&page=${page}`)
.then(res => {
this.setState({
photos: [...photos, ...res.data.photos.photo],
totalPages: res.data.photos.pages
});
if (page < totalPages) {
this.setState(prevState => ({ page: prevState.page + 1 }));
console.log(page); // returns 1 on the first call, but should return 2;
}
})
};
render() {
const { photos, page, totalPages, loading } = this.state;
return (
<div className={classes.Content}>
<InfiniteScroll
pageStart={page}
loadMore={this.getPhotos}
hasMore={page < totalPages}
loader={<Spinner />}
>
<Photos photos={photos} />
</InfiniteScroll>
</div>
);
}
}
and now, the problem is here:
if (page < totalPages) {
this.setState(prevState => ({ page: prevState.page + 1 }));
}
on the first function call, page is 1, but on my logic should be 2. On the next calls everything work good (page is 2, 3, 4 etc.). For now i'm loading two times the same photos. Can You help me?
As we know setState in react is asynchronous so you can’t see the value immediately when modified.
To get the updated value you should do something like below
getPhotos = () => {
const { photos, searchValue, page, totalPages } = this.state;
if (page < totalPages) {
this.setState(prevState => ({
page: prevState.page + 1
}), () => {
console.log(this.state.page);//will print 2
axios.get(`api_key=${API_KEY}&tags=${searchValue}&page=${this.state.page}`)
.then(res => {
this.setState({
photos: [...photos, ...res.data.photos.photo],
totalPages: res.data.photos.pages
});
})
});
}
}
this.setState(prevState => ({
score: prevState.score + 10,
rightAnswers: prevState.rightAnswers + 1,
currentQuestion: setTimeout(() => {
prevState.currentQuestion + 1
}, 2000)
}))
}
On button click I change the state. My goal is to have a delay in currentQuestion state change, during which I want to show certain status messages, yet I want to update the score right away without delays.
What's the proper way to do that?
PS: This variant doesn't work, it's for the overall representation of what I want to do.
Thanks.
You can do this multiple ways:
1) Make two calls to setState. React will batch any concurrent calls to setState into one batch update, so something like this is perfectly fine:
this.setState( prevState => ({
score: prevState.score + 10,
rightAnswers: prevState.rightAnswers + 1
}));
setTimeout( () => {
this.setState( prevState => ({
currentQuestion: prevState.currentQuestion + 1
}));
}, 2000);
2) You can use the setState callback to update state after your first call is finished:
this.setState(prevState => ({
score: prevState.score + 10,
rightAnswers: prevState.rightAnswers + 1
}), () => {
setTimeout( () => {
this.setState( prevState => ({
currentQuestion: prevState.currentQuestion + 1
}));
}, 2000);
});
First use setState to change score and question with some value like null so that you know its updating and then also set timeout after that.
class Example extends React.Component {
constructor(props) {
super(props)
this.state = {
score: 1,
question: "A"
}
}
update() {
this.setState(prev => ({
score: prev.score + 1,
question: null
}));
this.change = setTimeout(() => {
this.setState({question: "B"})
}, 2000)
}
render() {
let {score, question} = this.state;
let style = {border: "1px solid black"}
return (
<div style={style} onClick={this.update.bind(this)}>
<div>{score}</div>
<div>{question ? question : "Loading..."}</div>
</div>
)
}
}
ReactDOM.render( < Example / > , 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"></div>