React setState ne met pas à jour l'état

91

Donc j'ai ceci:

let total = newDealersDeckTotal.reduce(function(a, b) {
  return a + b;
},
0);

console.log(total, 'tittal'); //outputs correct total
setTimeout(() => {
  this.setState({dealersOverallTotal: total});
}, 10);

console.log(this.state.dealersOverallTotal, 'dealersOverallTotal1'); //outputs incorrect total

newDealersDeckTotal est juste un tableau de nombres, [1, 5, 9]par exemple this.state.dealersOverallTotalne donne pas le total correct mais le totalfait? J'ai même mis un délai d'attente pour voir si cela résolvait le problème. toute évidence ou devrais-je poster plus de code?

Le ver
la source
@Assan applaudit !!
Le ver
Outre ce qui est dit dans les réponses, vous enregistrez explicitement la valeur de l'état, avant d'appeler setState.
Felix Kling
1
@FelixKling non, j'appelle this.state après l' avoir défini. J'enregistre une variable avant. non?
Le ver
En raison du délai d'expiration, votre setStateest effectivement exécuté une fois que vous avez enregistré l'état. Je pense que ce que vous vouliez faire lors du débogage était de placer la console.logpièce à l'intérieur du délai d'expiration et à l' setStateextérieur.
Fabian Schultz

Réponses:

181

setState()est généralement asynchrone, ce qui signifie qu'au moment où vous console.logl'état, il n'est pas encore mis à jour. Essayez de mettre le journal dans le rappel de la setState()méthode. Il est exécuté une fois le changement d'état terminé:

this.setState({ dealersOverallTotal: total }, () => {
  console.log(this.state.dealersOverallTotal, 'dealersOverallTotal1');
}); 
Fabian Schultz
la source
1
En plus de cela, l'OP enregistre explicitement la valeur de l'état avant l' appel setState.
Felix Kling
Cela fonctionne aussi pour moi, dans le passé, j'ai utilisé ceci: `this.setState ({someVar: newValue}, function () {console.log (" forcer la mise à jour}); 'mais pour une raison quelconque, ce n'était pas inquiétant plus, quand j'ai mis à jour le code comme décrit ci-dessus, cela fonctionne. Une idée pourquoi?
Jozcar
@Jozcar Devrait fonctionner aussi, la syntaxe n'était pas correcte (parenthèses manquantes):this.setState({someVar: newValue},function(){ console.log("force update") });
Fabian Schultz
imgur.com/Ku0OjTl Veuillez me dire ce que je dois faire pour me débarrasser de ce problème.
Johncy
Cela ne fonctionne pas si vous utilisez un useStatehook dans un composant fonctionnel. Utilisez useEffectplutôt pour un effet après le rendu.
Hasan Sefa Ozalp le
16

setState est asynchrone. Vous pouvez utiliser la méthode de rappel pour obtenir l'état mis à jour.

changeHandler(event) {
    this.setState({ yourName: event.target.value }, () => 
    console.log(this.state.yourName));
 }
Mahesh Joshi
la source
10

Utiliser async / await

async changeHandler(event) {
    await this.setState({ yourName: event.target.value });
    console.log(this.state.yourName);
}
Dev01
la source
Je fais la même chose. A travaillé pour moi
prodeveloper
8

L' setState()opération est asynchrone et par conséquent, votre console.log()sera exécuté avant que setState()les valeurs ne mute et que vous voyez donc le résultat.

Pour le résoudre, enregistrez la valeur dans la fonction de rappel de setState(), comme:

setTimeout(() => {
    this.setState({dealersOverallTotal: total},
    function(){
       console.log(this.state.dealersOverallTotal, 'dealersOverallTotal1');
    });
}, 10)
Shubham Khatri
la source
JavaScript est toujours synchrone.
Santosh Singh
1
@santoshsingh vous avez une idée fausse. Les appels d'API, les délais d'expiration se produisent tous de manière asynchrone.
Shubham Khatri
Comme vous l'avez mentionné ci-dessus, javascript est asynchrone - ce n'est pas correct. C'est principalement synchrone et pour les cas, c'est asynchrone. stackoverflow.com/questions/2035645/…
Santosh Singh
@santoshsingh. Ohh c'était une erreur de ma part. N'a pas formé la phrase correctement
Shubham Khatri
utilisation très intelligente du callback pour s'assurer que l'état est mis à jour avant l'appel suivant!
user1204214
5

En cas de crochets, vous devez utiliser un useEffectcrochet.

const [fruit, setFruit] = useState('');

setFruit('Apple');

useEffect(() => {
  console.log('Fruit', fruit);
}, [fruit])
Siraj Alam
la source
Génial, cela fonctionne avec useEffect. Mais pourquoi en a-t-il besoin?
alex351 le
useEffects'exécute à chaque re-rendu, et si les éléments passés dans le tableau sont des variables d'état, sable changé. Ainsi, lorsque le fruit a changé et que le composant est à nouveau rendu, cet useEffect s'exécute.
Siraj Alam le
Je lance setFruit et console.log fruit en dehors de useEffect, et cela ne change pas. : /
alex351
3

Le setstate est asynchrone dans react, donc pour voir l'état mis à jour dans la console, utilisez le rappel comme indiqué ci-dessous (la fonction de rappel s'exécutera après la mise à jour de setstate)

entrez la description de l'image ici

La méthode ci-dessous n'est "pas recommandée" mais pour comprendre, si vous changez directement l'état, vous pouvez voir l'état mis à jour dans la ligne suivante. Je répète que ce n'est "pas recommandé"

entrez la description de l'image ici

mokesh moha
la source
MERCI ça y est, vous devez définir directement la variable
notacorn le
0

J'ai eu un problème lors de la définition de l'état de réaction plusieurs fois (il a toujours utilisé l'état par défaut). Suite à ce problème de réaction / github a fonctionné pour moi

const [state, setState] = useState({
  foo: "abc",
  bar: 123
});

// Do this!
setState(prevState => {
  return {
    ...prevState,
    foo: "def"
  };
});
setState(prevState => {
  return {
    ...prevState,
    bar: 456
  };
});
Arun Gopalpuri
la source
0

J'ai eu la même situation avec un code alambiqué, et rien des suggestions existantes n'a fonctionné pour moi.

Mon problème était que cela setStatese produisait à partir de la fonction de rappel, émise par l'un des composants. Et mon soupçon est que l'appel se produisait de manière synchrone, ce qui a empêché setStatedu tout de définir l'état.

En termes simples, j'ai quelque chose comme ça:

render() {
    <Control
        ref={_ => this.control = _}
        onChange={this.handleChange}
        onUpdated={this.handleUpdate} />
}

handleChange() {
    this.control.doUpdate();
}

handleUpdate() {
    this.setState({...});
}

La façon dont je devais "réparer" c'était de mettre doUpdate()en setTimeoutcomme ceci:

handleChange() {
    setTimeout(() => { this.control.doUpdate(); }, 10);
}

Pas idéal, mais sinon ce serait une refactorisation importante.

zmécanicien
la source