useState kanca ayarlayıcı durumun üzerine yanlış yazıyor


31

İşte sorun: Bir düğme tıklamasıyla 2 işlevi çağırmaya çalışıyorum. Her iki fonksiyon da durumu günceller (useState kancasını kullanıyorum). İlk işlev, değer1'i 'yeni 1' olarak doğru şekilde günceller, ancak 1 saniye sonra (setTimeout) ikinci işlev patlar ve değer 2'yi 'yeni 2' olarak değiştirir AMA! Değer1'i tekrar '1'e ayarlar. Bu neden oluyor? Şimdiden teşekkürler!

import React, { useState } from "react";

const Test = () => {
  const [state, setState] = useState({
    value1: "1",
    value2: "2"
  });

  const changeValue1 = () => {
    setState({ ...state, value1: "new 1" });
  };
  const changeValue2 = () => {
    setState({ ...state, value2: "new 2" });
  };

  return (
    <>
      <button
        onClick={() => {
          changeValue1();
          setTimeout(changeValue2, 1000);
        }}
      >
        CHANGE BOTH
      </button>
      <h1>{state.value1}</h1>
      <h1>{state.value2}</h1>
    </>
  );
};

export default Test;

durumunu başlangıçta kaydedebilir misiniz changeValue2?
DanStarns

1
Nesneyi iki ayrı aramaya ayırmanızı useStateveya bunun yerine kullanmanızı şiddetle tavsiye ederim useReducer.
Jared Smith

Evet - ikinci. UseState ()
Esben Skov Pedersen

const [state, ...]ve ayarlayıcıda ona atıfta bulunarak ... Her zaman aynı durumu kullanacaktır.
Johannes Kuhn

En iyi eylem şekli: useStateher biri "değişken" için bir tane olmak üzere 2 ayrı çağrı kullanın .
Dima Tisnek

Yanıtlar:


30

Kapaný cehenneme hoţ geldiniz . Bu sorun setState, her çağrıldığında stateyeni bir bellek başvurusu aldığı, ancak işlevlerin changeValue1ve changeValue2kapatılması nedeniyle eski ilk statebaşvuruyu koruduğu için oluşur .

setStateFrom changeValue1ve changeValue2en son durumu almak için bir çözüm bir geri arama (parametre olarak önceki durumuna sahip) kullanmaktır:

import React, { useState } from "react";

const Test = () => {
  const [state, setState] = useState({
    value1: "1",
    value2: "2"
  });

  const changeValue1 = () => {
    setState((prevState) => ({ ...prevState, value1: "new 1" }));
  };
  const changeValue2 = () => {
    setState((prevState) => ({ ...prevState, value2: "new 2" }));
  };

  // ...
};

Bu kapatma sorunu hakkında burada ve burada daha geniş bir tartışma bulabilirsiniz .


UseState hook ile geri çağrı belgelenmemiş bir özellik gibi görünüyor , işe yaradığından emin misiniz?
HMR

@HMR Evet, çalışıyor ve başka bir sayfada belgeleniyor. Buradaki "İşlevsel güncellemeler" bölümüne göz atın: reaktjs.org/docs/hooks-reference.html ("Yeni durum önceki durum kullanılarak hesaplanırsa, setState işlevini geçebilirsiniz")
Alberto Trindade Tavares

1
@AlbertoTrindadeTavares Evet, ben de belgelere bakıyordum, hiçbir şey bulamadım. Cevabınız için çok teşekkürler!
Bartek

1
İlk çözümünüz sadece "kolay bir çözüm" değil, doğru yöntemdir. İkincisi sadece bileşen bir singleton olarak tasarlanmış ve o zaman bile emin değilim çünkü devlet her zaman yeni bir nesne olur.
Scimonster

1
Teşekkürler @AlbertoTrindadeTavares! Güzel bir
José Salgado

19

İşlevleriniz şu şekilde olmalıdır:

const changeValue1 = () => {
    setState((prevState) => ({ ...prevState, value1: "new 1" }));
};
const changeValue2 = () => {
    setState((prevState) => ({ ...prevState, value2: "new 2" }));
};

Böylece, eylem başlatıldığında önceki durumu kullanarak mevcut durumdaki herhangi bir özelliği kaçırmamanızı sağlar. Ayrıca kapanışları yönetmek zorunda kalmazsınız.


6

Ne zaman changeValue2 çağrılır, devlet dönüşler açmasının durumuna geri böylece ilk devlet tutulur ve daha sonra value2mülkiyet yazılır.

Bir dahaki sefere changeValue2bundan sonra çağrılır, devleti tutar{value1: "1", value2: "new 2"} , böylece value1özelliğin üzerine yazılır.

setStateParametre için bir ok fonksiyonuna ihtiyacınız vardır .

const Test = () => {
  const [state, setState] = React.useState({
    value1: "1",
    value2: "2"
  });

  const changeValue1 = () => {
    setState(prev => ({ ...prev, value1: "new 1" }));
  };
  const changeValue2 = () => {
    setState(prev => ({ ...prev, value2: "new 2" }));
  };

  return (
    <React.Fragment>
      <button
        onClick={() => {
          changeValue1();
          setTimeout(changeValue2, 1000);
        }}
      >
        CHANGE BOTH
      </button>
      <h1>{state.value1}</h1>
      <h1>{state.value2}</h1>
    </React.Fragment>
  );
};

ReactDOM.render(<Test />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>


3

Olan şey, her ikisinin de changeValue1ve oluşturuldukları oluşturmachangeValue2 işleminin durumunu görmesidir , bu nedenle bileşeniniz ilk kez oluşturulduğunda bu 2 işlev şunları görür:

state= {
  value1: "1",
  value2: "2"
}

Düğmeye tıkladığınızda, changeValue1ilk olarak çağrılır ve durumu{value1: "new1", value2: "2"} beklendiği gibi değiştirir.

Şimdi, 1 saniye sonra changeValue2çağrılır, ancak bu işlev hala başlangıç ​​durumunu ( {value1; "1", value2: "2"}) görür, bu nedenle bu işlev durumu şu şekilde güncellediğinde:

setState({ ...state, value2: "new 2" });

Gördüğünüz sona: {value1; "1", value2: "new2"}.

kaynak

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.