Imagine a post that can be liked when pressing a button. This button modifies a remote database, so it will take a little while to associate the like to the specific post.
Now, if an user starts pressing the button so fast with this code:
state = {
isLiked: false,
}
handlePress = () => {
this.setState(
{
isLiked: !this.state.isLiked,
},
this.handleLike
);
};
handleLike = async () => {
const { postId } = this.props;
try {
console.log(isLiked ? "Liking" : "Disliking")
await db.processLike(postId);
} catch (err) {
// If an error has occurred, reverse the 'isLiked' state
this.setState({
isLiked: !this.state.isLiked,
});
// TODO - Alert the error to the user in a toast
console.log(err);
}
console.log("DONE");
};
As all is async, it is possible to see this situation:
Liking
Disliking
DONE <---------- Disliking done
DONE <---------- Liking done
I have thought to create a state "isLiking" to avoid running the code until all the async job has finished. Something like this:
state = {
isLiking: false,
isLiked: false,
}
handlePress = () => {
if (this.state.isLiking) return; <------------------------------------
this.setState(
{
isLiking: true, <------------------------------------
isLiked: !this.state.isLiked,
},
this.handleLike
);
};
handleLike = async () => {
const { postId } = this.props;
try {
console.log(isLiked ? "Liking" : "Disliking");
await db.processLike(postId);
} catch (err) {
// If an error has occurred, reverse the 'isLiked' state
this.setState({
isLiked: !this.state.isLiked,
});
// TODO - Alert the error to the user in a toast
console.log(err);
}
this.setState({ isLiking: false }); <------------------------------------
console.log("DONE");
};
With this all is going OK, but if the user press the button fast, he will not be able to see the GUI changes (the like button color (red if is liked, white if not)) until all the process described in the code above finishes.
I have also thought to make a debounced function (for the handlePress) like this:
export const debounce = (func, wait, immediate) => {
/*
Returns a function, that, as long as it continues to be invoked, will not
be triggered. The function will be called after it stops being called for
N milliseconds. If `immediate` is passed, trigger the function on the
leading edge, instead of the trailing.
*/
let timeout;
return function () {
let context = this,
args = arguments;
let later = function () {
timeout = null;
if (!immediate) func.apply(context, args);
};
let callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
...
debuncedHandlePress = debounce(this.handlePress, 500); // Now, when the button is pressed, it will call this function, instead of the original handlePress
But with this, the only thing I do is decrease the chance of getting messy results. That is, I still have the same problem as with the first code.
Any idea to do what I want in such a way that the results I get are ordered and avoiding the little wait for writing to the database?
Thank you.
The solution is to disable the button immediately. Using setState
, you cannot expect immediate update of isLinking
, and that's why you are annoyed.
One of the solution is to use flag variable
instead of using state
.
You can fix in this way.
state = {
isLiked: false,
}
constructor(props) {
this.isLiking = false; <------------------------------------
}
handlePress = () => {
this.isLiking = true; <------------------------------------
this.setState(
{
isLiked: !this.state.isLiked,
},
this.handleLike
);
};
handleLike = async () => {
const { postId } = this.props;
try {
console.log(isLiked ? "Liking" : "Disliking");
await db.processLike(postId);
} catch (err) {
// If an error has occurred, reverse the 'isLiked' state
this.setState({
isLiked: !this.state.isLiked,
});
// TODO - Alert the error to the user in a toast
console.log(err);
}
this.isLiking = false; <------------------------------------
console.log("DONE");
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With