Imaginez un post qui peut être aimé en appuyant sur un bouton. Ce bouton modifie une base de données distante, il faudra donc un peu de temps pour associer le like à la publication spécifique.

Maintenant, si un utilisateur commence à appuyer si vite sur le bouton avec ce 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");
  };

Comme tout est asynchrone, il est possible de voir cette situation :

J'aime

Je n'aime pas

FAIT <---------- Je n'aime pas fait

FAIT <---------- Aimer fait

J'ai pensé à créer un état "isLiking" pour éviter d'exécuter le code jusqu'à ce que tout le travail asynchrone soit terminé. Quelque chose comme ça:

 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");
  };

Avec cela, tout se passe bien, mais si l'utilisateur appuie rapidement sur le bouton, il ne pourra pas voir les changements de l'interface graphique (la couleur du bouton similaire (rouge si on aime, blanc sinon)) jusqu'à ce que tout le processus décrit dans le code finitions ci-dessus.

J'ai aussi pensé à faire une fonction anti-rebond (pour le handlePress) comme ceci :

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

Mais avec cela, la seule chose que je fais est de réduire les chances d'obtenir des résultats désordonnés. C'est-à-dire que j'ai toujours le même problème qu'avec le premier code.

Une idée pour faire ce que je veux de manière à ce que les résultats que j'obtienne soient ordonnés et en évitant le peu d'attente pour écrire dans la base de données ?

Je vous remercie.

5
Victor Molina 17 oct. 2020 à 18:43

1 réponse

Meilleure réponse

La solution est de désactiver le bouton immédiatement. En utilisant setState, vous ne pouvez pas vous attendre à une mise à jour immédiate de isLinking, et c'est pourquoi vous êtes ennuyé. L'une des solutions consiste à utiliser flag variable au lieu d'utiliser state.

Vous pouvez réparer de cette façon.

 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");
  };
5
Prime 17 oct. 2020 à 17:10