Zacznijmy może od typowego przykładu powielonego w wielu kursach: Licznika. Stwórz komponent, który ma dwa przyciski (plus i minus) oraz output
na wyświetlanie wyniku. Początkowo wartość wynosi 0, kliknięcie w przycisk odpowiednio zwiększa lub zmniejsza liczbę. Umiesz już obsłużyć kliknięcia, potrafisz też wyświetlać dane. Jak jednak je modyfikować?
Propsy są niemutowalne
Propsów nie da się zmienić z wnętrza komponentu. A jeśli spróbujesz to pewnie Ci się uda, ale będziesz mieć ogromne problemy — niespójne dane na ekranie, a może nawet jakieś błędy. Generalnie: Straszne rzeczy. Co do zasady: Propsów nie zmieniamy z wnętrza komponentu, do którego zostały one przekazane. I kropka.
Wchodzi state
A więc tutaj pojawia się słynny state. Do czego służy? Do przechowywania stanu komponentu. Ponadto, state można mutować dzięki funkcji setState
. A więc jest to dokładnie ten brakujący element układanki, którego poszukujemy! Tak, tak, to właśnie w state
będziesz przechowywać licznik, który chcesz zaimplementować.
Wszystko na temat stanu wyjaśniamy na szkoleniach. Jeśli coś jest dla Ciebie niejasne to zapisz się na szkolenie z React.
Jeszcze jedna mała uwaga: Do state
nie dobierzesz się w funkcyjnych komponentach. Stąd też ich nazwa: Stateless Functional Components. Potrzebna będzie klasa. Skoro to jest już jasne, weźmy się za pisanie kodu:
class App extends React.Component {
render() {
return (
<div>
<button>+</button>
<output>{this.state.counter}</output>
<button>-</button>
</div>
);
}
}
Tak mniej-więcej będzie wyglądała nasza funkcja render
. Jednak jeśli teraz odpalisz ten kod to dostaniesz w konsoli wyjątek, coś podobnego do Cannot read property 'counter' of null
. Chwila drapania się po głowie i… no jasne, przecież nigdzie nie podaliśmy czym w ogóle jest state
! Do tego potrzebny nam będzie konstruktor klasy. Dopisz na początku swojego komponentu:
constructor() {
super();
this.state = {counter: 0};
}
Przypomnę tylko, że jeśli klasa po czymś dziedziczy (tak jak tutaj po React.Component
) to wewnątrz konstruktora musisz wywołać super()
. Potem ustawiasz state
na taki, jaki ma on być domyślnie — zanim zostaną wykonane jakiekolwiek akcje przez użytkownika. Teraz aplikacja renderuje się poprawnie, aczkolwiek nic spektakularnego się jeszcze nie dzieje!
this
w React
Dopisujemy dwa onClick
do przycisków i dwie metody w klasie: Jedna do zwiększania, a druga do zmniejszania wartości w liczniku. Posłuży do tego funkcja setState
, w której odpowiednio ustawiamy licznik na (obecna wartość + 1
) lub (obecna wartość - 1
):
<button onClick={this.increment}>+</button>
increment() {
this.setState({
counter: this.state.counter + 1
})
}
Jednak po kliknięciu w przycisk dostajemy tylko błąd: Cannot read property 'setState' of undefined
. Cooo?
Wspominałem, że przy klasach pojawi nam się błąd związany z this
. Każdy kto zna JS widzi już w czym problem: this
w momencie wywołania funkcji increment
nie jest związane z instancją komponentu. Jak rozwiązać ten problem?
Jest kilka sposobów, które omówię później. Na razie weźmiemy najprostszy: bind
. Zmień kod w JSX:
<button onClick={this.increment.bind(this)}>+</button>
Woah, działa!
Ćwiczenie
Ćwiczenie: Dodaj dwa nowe liczniki. Pierwszy, który będzie zliczał wszystkie kliknięcia w przyciski (tzn. kliknięcie w +
i -
daje 0 na obecnym liczniku oraz 2 na nowym liczniku), oraz drugi, który będzie zliczał podwójne kliknięcia (tzw. double click) na elemencie z wynikiem. Jak wygląda teraz Twój state
? Czy napotkałaś/eś jakieś problemy, albo coś Cię zaskoczyło? Napisz o tym w komentarzu :)