Prevent Doubly Updating and Reverting State in React

Erik Aybar
InstructorErik Aybar

Share this video with your friends

Send Tweet

Optimistic UI updates come with challenges that can vary for every use case. In our scenario of “liking a tweet”, one challenge that we face is preventing doubly updating and reverting state in the event of the user clicking “like” in rapid succession. In the case of a request failure our current solution results in the false impression that the tweet was successfully liked.

One solution that fits our use case is to simply raise and lower a boolean flag to indicate whether a like tweet request is pending or not and short circuit our onClickLike method to "do nothing" if a request is already pending. Since we don’t require this information in our render method or on component state, we can simplify this further by making use of a class instance property to store this value.

Andrey Bakhvalov
Andrey Bakhvalov
~ 3 years ago

Hi! As I see the flag "likeRequestPending" don't allow to make parallels optimistic updates, because we will wait until a first request will complete or failure. Am I right?

Erik Aybar
Erik Aybarinstructor
~ 3 years ago

Hi Andrey, good catch and question! You're right that as-is likeRequestPending doesn't account for multiple/parallel requests.

If that did become a requirement, the current solution could be modified to support that. One solution is to change likeRequestPending from a boolean into an array of ids such as pendingTweetRequests to track pending requests for individual tweets by id. Example:

class App extends React.Component {
  // An array of tweet ids representing pending requests
  pendingTweetRequests = [];

  onClickLike = tweetId => {
    // 1) Is a request already pending for this tweet?
    if (this.pendingTweetRequests.includes(tweetId)) {
      return;
    }

    // ...

    // 2) Add this tweet to pendingTweetRequests
    this.pendingTweetRequests = this.pendingTweetRequests.concat(tweetId);

    likeTweetRequest(tweetId, !isLiked)
      // ...
      .then(() => {
        // 3) Remove tweet from pendingTweetRequests
        this.pendingTweetRequests = this.pendingTweetRequests
          .filter(id => id !== tweetId);
      });
  };

  // ...
}
Saloni Jain
Saloni Jain
~ 2 years ago

Scenario:

  1. Tweet is not liked by default.
  2. User clicks on like, we optimistically update to liked.
  3. while the first request is in progress, user realizes they want to revert so they click again to unlike, we do nothing and return as the first request is in progress.
  4. the user's second click is lost, and the UI does not behave as they expect it to.

Is this understanding correct ? If yes, is there an easy way to not let this happen?