web-dev-qa-db-fra.com

réagir le rappel de setState n'a pas l'état mis à jour

si monthOffset = 12, la condition est évaluée à true et met à jour l'état yearOffset à 2017 si yearOffset = 2018. Sur la base des documents de réaction et des autres réponses que j'ai lues, la fonction de rappel dans this.setState se déclenche après la mise à jour de l'état. Pourtant, la fonction console.log() affiche toujours 2018. J'ai essayé plusieurs méthodes d'implémentation de ce code en fonction du formulaire de réponses. d'autres questions connexes, mais la mienne ne fonctionne pas. Je ne sais pas pourquoi.

handleClick(e) {
  const { monthOffset, yearOffset } = this.state
  this.setState({ monthOffset: monthOffset - 1 })
  if ( monthOffset - 1 === 11 ) { this.setState((prevState) => { 
    return { yearOffset: prevState.yearOffset - 1 } },
    () => {console.log("yearOffset", yearOffset)}
  )}
  console.log("clicked")
}
3
Ryan Sam

Peut-être pourriez-vous simplifier votre logique de la manière suivante, afin d'éviter que plusieurs appels à setState ne provoquent des résultats inattendus:

handleClick(e) {

  const { monthOffset, yearOffset } = this.state

  // call setState once
  this.setState({ 

    // Always decrement month offset
    monthOffset : monthOffset - 1, 

    // Only decrement year offset if current month offset === 12
    yearOffset : (monthOffset === 12) ? yearOffset - 1 : yearOffset

  }, () => {

    console.log("state updated to", this.state)
  })

  console.log("clicked")
}
3
Dacre Denny

La documentation indique que le rappel fonctionne toujours, mais je sais par expérience qu'il ne renvoie pas toujours ce que vous attendez. Je pense que cela a quelque chose à voir avec l'utilisation d'objets mutables à l'intérieur de l'état même.

Docs: https://reactjs.org/docs/react-component.html#setstate

Vous ne pouvez pas compter complètement sur le rappel. Au lieu de cela, vous pouvez créer

var stateObject = this.state 

Apportez les modifications nécessaires à l'objet:

stateObject.monthOffset -= 1

puis définissez l'état comme ceci:

this.setState(stateObject);

De cette façon, vous avez une copie de la nextState à l'intérieur de stateObject

Pour clarifier: Vous souhaitez effectuer toute l'évaluation avant de définir l'état, procédez comme suit:

monthOffset -= 1

alors if (monthOffset === 12) yearOffset -=1;

alors var stateObj = {monthOffset: monthOffset, yearOffset: yearOffset}

alors this.setState(stateObj);


Dans la documentation: "Le deuxième paramètre de setState() est une fonction de rappel facultative qui sera exécutée une fois que setState est terminé et que le composant est restitué. Généralement, nous recommandons d'utiliser ComponentDidUpdate () pour une telle logique . "

En gros, si vous voulez obtenir le prochain état, vous devez en avoir une copie dans votre fonction qui appelle setState() ou vous devez obtenir le nextState à partir de componentDidUpdate


Après: Tout ce que vous transmettez à setState() en tant que paramètre est passé par référence (pas par valeur). Ainsi, si vous avez un objet SearchFilters: {} dans votre état et dans votre appel à setState(), vous avez 

setState({SearchFilters: DEFAULT_SEARCH_FILTERS}); // do not do this

Vous avez peut-être défini SearchFilters sur DEFAULT_SEARCH_FILTERS dans le but d'effacer un formulaire appelé "Filtres de recherche", mais vous aurez en fait défini DEFAULT_SEARCH_FILTERS (une constante) sur SearchFilters, en effaçant votre DEFAULT_SEARCH_FILTERS.

Comportement prévisible? À vous de me dire.

2
ihodonald

Il existe deux modèles courants pour appeler setState dans React: objet setState et "fonctionnel setState" . La fonction setState est généralement utilisée lorsque l'état actuel (ou "état précédent", ou ce que vous souhaitez appeler l'ancien état) est appelé dans l'appel setState. Ceci est fait parce que setState est asynchrone et que, par conséquent, setStates suivant peut parfois être exécuté avant que React n'ait réussi à terminer le premier cycle setState.

Vous avez utilisé l'état existant dans votre appel setState. Ce serait donc un endroit approprié pour utiliser la fonction setState. Remplacer

this.setState({ monthOffset: monthOffset - 1 })

avec

this.setState(monthOffset => {return {monthOffset: monthOffset - 1}})

Si vous êtes comme moi la première fois que j'ai vu cela, vous vous dites peut-être: "Hein? Comment est-ce différent de ce que j'ai?" La différence est que lorsque setState se voit attribuer une fonction au lieu d'un objet, il met la mise à jour en file d'attente au lieu de passer par son processus de résolution habituel, ce qui garantit que les opérations sont effectuées dans l'ordre.

Ou vous ne pensez peut-être pas cela; vous avez réellement utilisé setState fonctionnel dans votre deuxième appel setState. En l'utilisant dans votre premier aussi, vous vous assurez que les choses sont correctement placées dans la file d'attente.

1
Andy Taton