Counter z useState() nie zadziała

0
import "./styles.css";
import { useState } from "react";

const App = () => {
  const [counter, setCounter] = useState({ count: 0 });

  const incrementUp = () => {
    // setCounter((counter) => {
    //   counter.count = counter.count + 1;
    //   return counter;
    // });

    setCounter((counter) => {
      return {...counter, count: counter.count + 1}
    })

  };
  return (
    <div>
      <div>
        {counter.count}
        <button onClick={incrementUp}>increment Up</button>
      </div>
    </div>
  );
};

export default App;

Dlaczego ten counter w połączeniu z useState() w React nie zadziała ? Chodzi mi o ten zakomentowany kod. Rozwiązanie znalazłem jednak sam nie wiem czemu do końca ta pierwsza wersja (to co jest w komentarzu) nie zadziałała.

1

Jeśli w stanie komponentu przechowujemy obiekt / tablicę to funkcja po prostu musi zwrócić nową wartość.

setCounter((counter) => {
  counter.count = counter.count + 1;
  
  return { ...counter };
});
0
Xarviel napisał(a):

Jeśli w stanie komponentu przechowujemy obiekt / tablicę to funkcja po prostu musi zwrócić nową wartość.

setCounter((counter) => {
  counter.count = counter.count + 1;
  
  return { ...counter };
});

Ale czemu tak? Ma to jakiś związek z referencją obiektu ?

1

W zakomentowanym kodzie zmieniasz/mutujesz bezpośrednio obiekt, a w świecie React tak się nie robi, tylko się zwraca nowe wartości (tworzysz nowy obiekt stanu).

Przy czym tak naprawdę w tym przykładzie nie musisz opakowywać tego w obiekt. Tak by wystarczyło:

const [counter, setCounter] = useState(0);

wtedy counter wynosi na początku zero, a setCounter ustawia bezpośrednio liczbę.

0
sajek587 napisał(a):

Ma to jakiś związek z referencją obiektu ?

Tak

Można powiedzieć, że w tym przykładzie jest podobna sytuacja.

const counter = { count: 1 };
const newCounter = counter;
// const newCounter = { ...counter };

newCounter.count++;

console.dir(counter);
console.dir(newCounter);

console.dir(counter === newCounter);

if (counter.count !== newCounter.count) {
  // to się nie wykona, bo zmieniliśmy obie wartości
}

Zmieniamy tylko jedną wartość, ale finalnie wpłynie to na oba obiekty counter.

React w takich sytuacjach ma problem z wykryciem, że stan komponentu uległ zmianie i nie renderuje go ponownie.

0
LukeJL napisał(a):

W zakomentowanym kodzie zmieniasz/mutujesz bezpośrednio obiekt, a w świecie React tak się nie robi, tylko się zwraca nowe wartości (tworzysz nowy obiekt stanu).

Przy czym tak naprawdę w tym przykładzie nie musisz opakowywać tego w obiekt. Tak by wystarczyło:

const [counter, setCounter] = useState(0);

wtedy counter wynosi na początku zero, a setCounter ustawia bezpośrednio liczbę.

Tylko ja to wiem, a nadal nie mogę zrozumieć dlaczego ten pierwszy sposób nie zadziała. Czy w pierwszym przykładzie po prostu tworzę jakiś nowy obiekt, którego później się nie wykorzystuje ?

0

No jak zmienisz obiekt na żywca, to React nie będzie wiedział, że go zmieniłeś. Bo gdzieś tam sobie porównuje obiekty i widzi, że ten sam obiekt.

W zasadzie to, o co pytasz, to fundamenty i jest na ten temat masę informacji w necie np. https://beta.reactjs.org/learn/updating-objects-in-state

poza tym hasła: mutable / immutable odnośnie JSa jako takiego. Mutowalność masz jak zmieniasz obiekt w miejscu, a niemutowalność zakłada, że obiekty mają się nie zmieniać i jak chcesz nową wartość, to tworzysz nowy obiekt z innymi wartościami w środku.

Czy w pierwszym przykładzie po prostu tworzę jakiś nowy obiekt, którego później się nie wykorzystuje ?

Odwrotnie, przecież w drugim przykładzie tworzysz nowy obiekt

 setCounter((counter) => {
      return {...counter, count: counter.count + 1}
  })

czyli:

 {...counter, count: counter.count + 1}

to nowy obiekt z przekopiowaną wartością tego, co jest w counter, oraz z ustawioną właściwością count na counter.count + 1

W tym kodzie (co był zakomentowany):

    setCounter((counter) => {
       counter.count = counter.count + 1;
       return counter;
    });

nie tworzysz nowego obiektu, tylko zmieniasz istniejący przecież (czego nie powinieneś robić, jeśli korzystasz ze stanu lokalnego w React. Chociaż istnieją zewnętrzne biblioteki do zarządzania stanem takie jak Mobx, które umożliwiają taki właśnie mutowalny styl pisania)

0
LukeJL napisał(a):

No jak zmienisz obiekt na żywca, to React nie będzie wiedział, że go zmieniłeś. Bo gdzieś tam sobie porównuje obiekty i widzi, że ten sam obiekt.

W zasadzie to, o co pytasz, to fundamenty i jest na ten temat masę informacji w necie np. https://beta.reactjs.org/learn/updating-objects-in-state

poza tym hasła: mutable / immutable odnośnie JSa jako takiego. Mutowalność masz jak zmieniasz obiekt w miejscu, a niemutowalność zakłada, że obiekty mają się nie zmieniać i jak chcesz nową wartość, to tworzysz nowy obiekt z innymi wartościami w środku.

Czy w pierwszym przykładzie po prostu tworzę jakiś nowy obiekt, którego później się nie wykorzystuje ?

Odwrotnie, przecież w drugim przykładzie tworzysz nowy obiekt

 setCounter((counter) => {
      return {...counter, count: counter.count + 1}
  })

czyli:

 {...counter, count: counter.count + 1}

to nowy obiekt z przekopiowaną wartością tego, co jest w counter, oraz z ustawioną właściwością count na counter.count + 1

W tym kodzie (co był zakomentowany):

    setCounter((counter) => {
       counter.count = counter.count + 1;
       return counter;
    });

nie tworzysz nowego obiektu, tylko zmieniasz istniejący przecież (czego nie powinieneś robić, jeśli korzystasz ze stanu lokalnego w React. Chociaż istnieją zewnętrzne biblioteki do zarządzania stanem takie jak Mobx, które umożliwiają taki właśnie mutowalny styl pisania)

Ok, czyli w tym kodzie zakomentowany zmieniam ten sam obiekt i przez to React nie widzi żadnych zmian, bo referencja do obiektu się nie zmienia ?

0
sajek587 napisał(a):

Ok, czyli w tym kodzie zakomentowany zmieniam ten sam obiekt i przez to React nie widzi żadnych zmian, bo referencja do obiektu się nie zmienia ?

Dokładnie tak

1

Jeszcze warto dodać, że argumentem w tym callbacku w funkcji set jest poprzedni stan. Czyli odwołując się do tej wartości to dosłownie odwołujesz się do poprzedniego stanu. I tym samym zwracasz poprzedni stan, więc praktycznie masz wciąż ten sam stan (czyt. 0) :)

0
madaucka napisał(a):

Jeszcze warto dodać, że argumentem w tym callbacku w funkcji set jest poprzedni stan. Czyli odwołując się do tej wartości to dosłownie odwołujesz się do poprzedniego stanu. I tym samym zwracasz poprzedni stan, więc praktycznie masz wciąż ten sam stan (czyt. 0) :)

Tu nie o to chodzi, zmiana wartosci NIE MA nic wspolnego z porownaniem referencji.

0
kambiodolor napisał(a):
madaucka napisał(a):

Jeszcze warto dodać, że argumentem w tym callbacku w funkcji set jest poprzedni stan. Czyli odwołując się do tej wartości to dosłownie odwołujesz się do poprzedniego stanu. I tym samym zwracasz poprzedni stan, więc praktycznie masz wciąż ten sam stan (czyt. 0) :)

Tu nie o to chodzi, zmiana wartosci NIE MA nic wspolnego z porownaniem referencji.

Otóż to. Jak wspomniałem argumentem w callbacku jest referencja do poprzedniego stanu. Do wartości w tym kontekście chodziło mi o stan z aktualna (poprzednią) referencją

1 użytkowników online, w tym zalogowanych: 0, gości: 1