- React.js: Wprowadzenie do kursu od podstaw
- Poznaj React.js
- Pierwszy komponent w React.js
- Props czyli atrybuty w React.js
- Podział na komponenty w React.js
- Klasy jako komponenty React.js
- Interakcja z komponentami React.js
- Stan komponentów React.js
- State w React.js 2
- Metody cyklu życia komponentu w React.js
- React.js w przykładach: filtrowanie statycznej listy
- Tworzenie aplikacji React.js dzięki create-react-app
- React.js na GitHub Pages dzięki create-react-app
- Testowanie aplikacji React.js — podstawy Enzyme
- Testowanie React.js w Enzyme — props, state i interakcje
- Poprawne bindowanie funkcji w React.js
- Odpowiadam na pytania: Babel, ECMAScript, destrukturyzacja, onClick, className
- Komunikacja pomiędzy komponentami w React.js
- Komunikacja z API w React.js
- Formularze w React.js — kontrolowane komponenty
- Formularze w React.js — niekontrolowane komponenty
- Odpowiadam na pytania: props, nawiasy klamrowe, funkcje vs klasy, import react
- TDD w React.js z pomocą react-testing-library
- Flux i Redux: globalny store i jednokierunkowy przepływ danych
- React + Redux — kurs: wprowadzenie i podstawy
- React + Redux — filtrowanie listy, proste selektory
- Projektowanie komponentów: Presentational & Container Components
- Asynchroniczność w Redux: redux-thunk
- Kiedy używać state, a kiedy Redux?
- Nowe metody cyklu życia: getDerivedStateFromProps i getSnapshotBeforeUpdate
- Leniwe ładowanie komponentów w React dzięki import
- Higher Order Reducers — Redux i powtarzanie kodu
- React Hooks — wprowadzenie i motywacja
- React Hooks: useState, czyli stan w komponentach funkcyjnych
- React Hooks: useState — wiele stanów, callbacki i inne niuanse
- React Hooks: useEffect — efekty uboczne w komponencie
- React Hooks a żądania do API
- useReducer — przenoszenie logiki poza komponent
- useMemo, useCallback, czyli rozwiązanie problemów ze zmieniającymi się propsami
- Wady React Hooks
- React Hooks: Piszemy własne hooki!
Przy okazji omawiania komponentów będących klasami w React.js, wspomniałem też o możliwości korzystania z metod cyklu życia (lifecycle methods). Chciałbym do tego tematu teraz powrócić. Pokażę jakie metody cyklu życia definiuje React.js, do czego one służą i kiedy mogą się przydać.
Cykl życia komponentu
Bardzo dokładnie omawiamy cykl życia komponentów na szkoleniach. Poznaj React na szkoleniu z Type of Web!
Każdy komponent ma pewien określony cykl życia. Na odpowiednich etapach wywoływane są też jego metody cyklu życia. Możemy je podzielić ogólnie na dwie grupy:
- z nazwami zaczynającymi się od
will
— wywoływane zanim się coś wydarzy - z nazwami zaczynającymi się od
did
— wywoływane po tym jak coś się zdarzyło
Oto kolejne etapy życia komponentów:
Montowanie
Komponent jest tworzony i dodawany do drzewa Dom.
constructor(props)
— jeśli definiujesz konstruktor to nie zapomnij wywołać w nimsuper(props)
na samym początku; możesz też tam ustawićthis.state = {…}
bez konieczności używaniasetState
. To będzie początkowy stan aplikacji. Inne operacje (efekty uboczne, subskrypcje) powinny się znaleźć wcomponentDidMount
componentWillMount()
— wywoływany tuż przed zamontowaniem komponentu; ta metoda raczej Ci się nie przydarender()
— funkcja, która powinna zwrócić jeden z możliwych wyników:- element (JSX)
- tablicę elementów (JSX)
- string lub liczbę
null
(nic się nie renderuje)- boolean (nic się nie renderuje)
- Portal (stworzony przez
ReactDOM.createPortal(…)
)
componentDidMount()
— wywoływana po zamontowaniu komponentu; to dobre miejsce na jakiekolwiek funkcje polegające na DOM lub na subskrypcje (nie zapomnij o usunięciu subskrypcji wcomponentWillUnmount()
!)
Aktualizacja
Update może zostać wywołany gdy zmieniają się props lub state.
componentWillReceiveProps(nextProps)
— wywoływana m.in. gdy zmieniają się propsy (np. gdy element-rodzic je zmienia); warto porównaćthis.props
znextProps
i sprawdzić czy rzeczywiście coś się zmieniło (bo nie zawsze musi…)shouldComponentUpdate(nextProps, nextState)
— wywoływana zawsze przedrender()
; jeśli z tej funkcji zwróciszfalse
torender()
nie zostanie wykonany, a widok się raczej nie przerenderuje — można to wykorzystać do optymalizowania aplikacji; UWAGA: komponenty-dzieci nadal mogą się przerenderować np. gdy zmieni się ichstate
, a w przyszłości React będzie traktował funkcjęshouldComponentUpdate()
tylko jako wskazówkę, a nie wyznacznik — wrócę do tego tematu jeszczecomponentWillUpdate(nextProps, nextState)
— wywoływana tuż przedrender()
— pamiętaj aby nie modyfikowaćstate
wewnątrz tej funkcjirender()
— j.w.componentDidUpdate(prevProps, prevState)
— wywoływana od razu po renderze; dobre miejsce na zmiany w DOM (jeśli takowe są potrzebne)
Odmontowanie
Wywoływane gdy komponent jest usuwany z DOM
componentWillUnmount()
— wywoływana przed usunięciem komponentu z DOM; dobre miejsce na „posprzątanie” po sobie — usunięcie timerów, subskrypcji, zmian w DOM itd.
Łapanie błędów
componentDidCatch(error, info)
— wywoływana gdy pojawi się błąd w czasie renderowania, wywoływania metod cyklu życia lub w konstruktorze — zagnieżdżonych komponentów; błędy w samym komponencie nie są tutaj łapane (zostaną złapane w komponencie-rodzicu)
Przykład
Oto prosta aplikacja, która pokazuje ważny przykład używania metod cyklu życia. Są to dwa komponenty, App
odpowiada za pokazywanie i ukrywanie (po kliknięciu w przycisk) drugiego komponentu. Drugi komponent to Date
, który co sekundę aktualizuje stan i wyświetla aktualną datę i godzinę. Wykorzystane zostały takie lifecycle methods:
constructor()
— ustawianie początkowego stanucomponentDidMount()
— rozpoczęcie odliczania (setInterval
)componentWillUnmount()
— usunięcie odliczania (clearInterval
)
Warto też zwrócić uwagę na sposób w jaki komponent App
renderuje komponent Date
w zależności od this.state.dateVisible
:
<div>
{this.state.dateVisible && <DateComponent />}
</div>
See the Pen Metody cyklu życia komponentu w React.js by Michał Miszczyszyn (@mmiszy) on CodePen.
Jeśli chcesz na bieżąco śledzić kolejne części kursu React.js to koniecznie polub mnie na Facebooku i zapisz się na newsletter.
Ćwiczenie
Ćwiczenie: Stwórz dwa komponenty (rodzic i dziecko). Oto wymagania:
Rodzic pozwala na ustawienie (input
+button
+onClick
+setState
) jakiejś wartości liczbowej i przekazuje ją do dziecka jako props.Dziecko ma początkowo wyświetlać liczbę podaną od rodzica, a dodatkowo ma umożliwiać zwiększanie i zmniejszanie tej liczby (button
+onClick
+setState
).W momencie gdy rodzic ustawi liczbę, dziecko powinno zresetować swój stan do podanej liczby (componentWillReceiveProps
).
Aktualizacja 11.02.2019 r.: Przekreśliłem zadania powyżej. Miały one wyłącznie charakter dydaktyczny w celu poznania metody cyklu życia componentWillReceiveProps
, ale wiele osób bardzo je sobie zapamiętało i stosowało podobny wzorzec w swoich aplikacjach. Poza tym React oznaczył tę metodę jako przestarzałą i niezalecaną. Zajrzyj do tego wpisu:
Nowe metody cyklu życia: getDerivedStateFromProps i getSnapshotBeforeUpdate
Sugerowane tutaj przeze mnie rozwiązanie było błędne. Prawidłowym rozwiązaniem problemu powyżej jest przeniesienie stanu dziecka wyżej — do rodzica (tzw. lifting state up). W ten sposób rodzic kontroluje stan obu inputów, a dziecko informuje go o zmianach, które chce wprowadzić. Dzięki temu unika się kilku częstych problemów: z rerenderem (który mógłby prowadzić do skasowania się wartości z inputów), z koniecznością wykrywania zmian (przez niezalecany componentWillReceiveProps), czy choćby z niejasnym przepływem danych (dane do dziecka od rodzica spływają w dół, ale tylko czasem… a dziecko nigdy nie informuje rodzica o zmianach — to niedobrze!). Oto zaktualizowana treść ćwiczenia:
Zaktualizowane ćwiczenie
Ćwiczenie: Stwórz dwa komponenty (rodzic i dziecko). Oto wymagania:
- Rodzic pozwala na ustawienie w inpucie jakiejś wartości liczbowej i przekazuje ją do dziecka.
- Dziecko ma początkowo wyświetlać liczbę podaną od rodzica, a dodatkowo ma umożliwiać zwiększanie i zmniejszanie tej liczby.
- W momencie, gdy rodzic ustawi liczbę, dziecko powinno zresetować swój stan do podanej liczby.
- Podpowiedź: Zastosuj tutaj lifting state up i stan obu komponentów trzymaj w rodzicu.
Napisz w komentarzu czy się udało. A jeśli masz jakieś wątpliwości albo nawet nie wiesz jak zacząć — odezwij się! Pomogę!
- React.js: Wprowadzenie do kursu od podstaw
- Poznaj React.js
- Pierwszy komponent w React.js
- Props czyli atrybuty w React.js
- Podział na komponenty w React.js
- Klasy jako komponenty React.js
- Interakcja z komponentami React.js
- Stan komponentów React.js
- State w React.js 2
- Metody cyklu życia komponentu w React.js
- React.js w przykładach: filtrowanie statycznej listy
- Tworzenie aplikacji React.js dzięki create-react-app
- React.js na GitHub Pages dzięki create-react-app
- Testowanie aplikacji React.js — podstawy Enzyme
- Testowanie React.js w Enzyme — props, state i interakcje
- Poprawne bindowanie funkcji w React.js
- Odpowiadam na pytania: Babel, ECMAScript, destrukturyzacja, onClick, className
- Komunikacja pomiędzy komponentami w React.js
- Komunikacja z API w React.js
- Formularze w React.js — kontrolowane komponenty
- Formularze w React.js — niekontrolowane komponenty
- Odpowiadam na pytania: props, nawiasy klamrowe, funkcje vs klasy, import react
- TDD w React.js z pomocą react-testing-library
- Flux i Redux: globalny store i jednokierunkowy przepływ danych
- React + Redux — kurs: wprowadzenie i podstawy
- React + Redux — filtrowanie listy, proste selektory
- Projektowanie komponentów: Presentational & Container Components
- Asynchroniczność w Redux: redux-thunk
- Kiedy używać state, a kiedy Redux?
- Nowe metody cyklu życia: getDerivedStateFromProps i getSnapshotBeforeUpdate
- Leniwe ładowanie komponentów w React dzięki import
- Higher Order Reducers — Redux i powtarzanie kodu
- React Hooks — wprowadzenie i motywacja
- React Hooks: useState, czyli stan w komponentach funkcyjnych
- React Hooks: useState — wiele stanów, callbacki i inne niuanse
- React Hooks: useEffect — efekty uboczne w komponencie
- React Hooks a żądania do API
- useReducer — przenoszenie logiki poza komponent
- useMemo, useCallback, czyli rozwiązanie problemów ze zmieniającymi się propsami
- Wady React Hooks
- React Hooks: Piszemy własne hooki!
Poniżej rozwiązanie do ćwiczenia:
https://pastebin.com/BtTwq70t
Mały refaktor dla potomnych:
Niestety sama nie wpadłam na rozwiązanie ale przepisywanie kodu dla zrozumienia tez uczy 🙂
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
input: null,
forwardValue: null
}
this.handleClick = this.handleClick.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({ input: event.target.value });
}
handleClick() {
this.setState({ forwardValue: this.state.input });
}
render() {
return(
Propagate to child
{this.state.forwardValue && }
);
}
}
class Child extends React.Component {
constructor(props) {
super(props);
this.state = {
fromParent: Number(props.value),
current: Number(props.value)
}
this.handleIncrement = this.handleIncrement.bind(this);
this.handleDecrement = this.handleDecrement.bind(this);
}
componentWillReceiveProps(nextProps) {
if (nextProps.value != this.state.fromParent) {
this.setState({
fromParent: Number(nextProps.value),
current: Number(nextProps.value),
})
}
}
handleIncrement() {
this.setState({ current: this.state.current + 1 })
}
handleDecrement() {
this.setState({ current: this.state.current – 1 })
}
render() {
return(
{this.state.current}
+
–
);
}
}
ReactDOM.render(
,
document.getElementById(’root')
);
Ten kod już nie działa, wywala masę błędów 🙂
Cześć,
Mam dwa pytanie dotyczące powyższego kodu.
Pierwsze dotyczy tego fragmentu z komponentu Child:
componentWillReceiveProps(nextProps) {
if (nextProps.value != this.state.fromParent) {
Co jest tu przekazywane jako 'nextProps'?
Drugie pytanie dotyczy tego fragmentu: } }
{this.state.forwardValue &&
Czym różni się zapis:
{
Kurs świetny 🙂
Pozdrowienia,
Agata
Jako
nextProps
są przekazywane nowe propsy. Podthis.props
dostępne są stare, te przed zmianą.Ale muszę zauważyć, że od React 16.3 nie jest zalecane używanie tej funkcji w ogóle — zaktualizuję ten wpis na dniach!
{this.state.forwardValue && <child value="{this.state.forwardValue}/">}
oznacza, że jeślithis.state.forwardValue
nie będzie (a konkretnie to jeśli jest ono falsy value) to komponentchild
w ogóle nie zostanie wyrenderowany.componentWillReceiveProps(nextProps) {
if (nextProps.value != this.state.fromParent) {
this.setState({
fromParent: Number(nextProps.value),
current: Number(nextProps.value),
})
}
}
Dlaczego tutaj mamy instrukcje warunkową?
Bo chcemy ustawić state tylko jeśli props.value się zmieniło.
Świetne, czytelne rozwiązanie 🙂
Czy mogłabym poprosić o wytłumaczenie fragmentów kodu ? Staram się zrozumieć całość ale w tych miejscach mam problem, nie chcę iść dalej bo będę miała braki . Jestem początkująca i staram się zrozumieć podstawy.
Pytanie : co się kryje pod „Number(props.value)” i dlaczego tak sie to pisze, oraz jesli chodzi o drugi fragment dlaczego akurat nextProps i skąd funkcja wie czym ona jest skoro nigdzie wczesniej tego nie ma. Poźniej rozumiem, iż porównuje sie wartości.
this.state = {
fromParent: Number(props.value),
current: Number(props.value),
}
componentWillReceiveProps(nextProps) {
if (nextProps.value != this.state.fromParent) {
this.setState({
fromParent: Number(nextProps.value),
current: Number(nextProps.value),
})
}
Czy teraz wszystko jasne? 🙂
Dzięki!
Tak, myślę że tak 🙂
Number
to jedna z podstawowych funkcji w JS, służy do zamiany czegoś na liczbę. W tym przypadku propsy są stringami, więc autor chciał zmienić je na liczby.Co do
componentWillReceiveProps
— ta funkcja będzie wywoływana przez samego Reacta w momencie gdy zmienią się propsy. Zostanie do niej przekazany argument z nowymi propsami (zmienionymi), dzięki czemu można zareagować jakoś na zmiany. Nazwa argumentunextProps
pochodzi z dokumentacji i tak się przyjęło, ale oczywiście nie ma to żadnego znaczenia. Nazwy argumentów w funkcjach w JS mogą być zupełnie dowolne.Hej, a dlaczego nie ma Number(this.props.input) ? Czy takie oznaczenie
this.state = {
fromParent: Number(props.value),
current: Number(props.value),
}
Powoduje, że Kopiowane są wszytskie proppsy poprzednie i najnowsze ?
Nigdzie nie było
props.input
, więc nie wiem do czego to się odnosi.Zapis
this.state = {
fromParent: Number(props.value),
current: Number(props.value),
}
oznacza, że tworzony jest obiekt
state
, w którym są 2 własności:fromParent
icurrent
. Obie mają tę samą wartośćNumber(props.value)
.Dziękuję, chodziło mi oczywiście o Number(props.value)
Cześć. Nie rozumiem za bardzo, dlaczego stosuje się constructor. Widziałem przykłady, gdzie ustawiano stan początkowy bez constructora. Od czego zależy jego użycie?
A sam kurs bardzo fajny:)
Jeśli używasz klas to zawsze ustawia się początkowy state w konstruktorze. Mogłeś widzieć taki zapis:
To jest składnia, która jeszcze nie jest częścią ECMAScript, jest tylko propozycją — ale działa z create-react-app (dzięki Babelowi). Oznacza to to samo co taki zapis:
No i wszystko jasne, dzięki:)
Cześć, czy mógłby ktoś wytłumaczyć czym jest subskrypcja w React? Może być na jakimś przykładzie. Z góry dzięki 🙂
W samym React nie ma żadnych subskrypcji. Chodziło o jakieś inne biblioteki i czynności, np. połączenie z WebSockets albo pobranie danych z API, albo też subskrybowanie Observable… wszelkie tego typu rzeczy 🙂
Dlaczego w przykładzie z pokazywaniem daty w komponencie DateComponent w metodzie render() dla zmiennej dateStr sprawdzamy najpierw czy zmienna this.state.date istnieje? Przecież przy utworzeniu obiektu w konstruktorze zostaje ona zdefiniowana.
Bez powodu, wydaje się to niepotrzebne 🙂
[…] Metody cyklu życia komponentu w React.js · 28 May 2018 […]
Ktoś wie czemu w przykładzie musimy użyć tego bindowania ?
window.setInterval(this.updateDate.bind(this), 1000);
Bez tego zmieniłby się kontekt w funkcji updateDate. Tutaj Wojtek dobrze to wyjaśnia: https://typeofweb.com/2017/11/14/this-js-kontekst-wywolania-funkcji/
Michał,
Kawał dobrej roboty przy tworzeniu tego kursu – zaciekawiłeś mnie i lekcja po lekcji dotarłem do tego miejsca, a mam nadzieję przedostać się do samego końca.
Moja odrobiona praca domowa: https://codepen.io/anon/pen/gdZqdR
Moja wersja zadania z przykładu 🙂
https://jsbin.com/yaviqitute/edit?html,output
Starałem się napisać to samemu ale wspomagałem się podglądem kodu z przykładu co do użycia nowych funkcji 🙂
Mega Kurs ! 🙂
https://codepen.io/Rotarepmi/pen/vrNaEO
Moje podwójne rozwiązanie, także z użyciem getDerrivedStateFromProps 🙂
Bardzo ładnie 😉
Chciałbym zapytać, kiedy używać wspomnianej w poprzednim wpisie updater function, a kiedy przekazywać do setState() obiekt z wartościami, które chcemy zaktualizować.
Do tej port zauważyłem trzy różne typy sytuacji:
1) Updater:
https://pastebin.com/GEBCsjmF
2) Updater:
https://pastebin.com/3MC9vATf
3) I tutaj mam problem, bo chciałbym wykorzystać event, analogicznie jak wyżej ale rzuca mi nulla:
https://pastebin.com/hqa3t2ax
W 2 i 3 przypadku możesz użyć po prostu obiektu. Funkcji używaj wtedy, gdy ustawiane dane zależą od aktualnie ustawionych w state.
event.target.value będący nullem nie ma związku ze sposobem ustawiania state.
Przy okazji: Metoda componentWillReceivProps jest deprecated i będzie usunięta z Reacta.
PRAWIE się udało Mój kod:
class App extends React.Component {
constructor(props) {
super(props);
this.state = { input: 0, number: 0};
}
onInputChange(e) {
this.setState({input: Number(e.target.value)});
}
onButtonClick() {
this.setState({number: this.state.input});
}
render() {
return (
);
}
}
class ChildComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
childNumber: Number(props.number)
};
}
componentWillReceiveProps(newProps){
this.setState({childNumber: newProps.parentNumber});
}
increase(){
this.setState({childNumber: this.state.childNumber + 1});
}
decrease(){
this.setState({childNumber: this.state.childNumber - 1});
}
render() {
return (
) , document.getElementById("app"));
}
}
ReactDOM.render(
Nie wiem tylko, dlaczego przy zmienianiu liczby w inpucie od razu zmienia mi liczbę w outpucie na taką, jak była ostawiona wcześniej…. :/
@łaśnie edytowałem ten artykuł — koniecznie zobacz pod koniec od „Aktualizacja 11.02.2019 r”!
Poniżej moje rozwiązanie, korzystam z pluginu do babelu, który rozszerza właściwości klas, dlatego składnia może się różnić od składni podanej przez innych 🙂
import React from 'react';
class BtwComponentApp extends React.Component {
state = {
number: 0,
numberPassed: 0
};
onInputChange = (e) => {
const inputChangeVariable = parseInt(e.target.value);
this.setState({
number: inputChangeVariable
});
};
passParent = () => {
this.setState({
numberPassed: this.state.number
})
};
render() {
return (
);
}
}
class BtwChild extends React.Component {
state = {
childNum: 0
};
increaseNumber =() => {
this.setState((prevState) => ({
childNum: prevState.childNum+1
}))
};
componentDidUpdate() {
console.log(’will');
}
componentWillReceiveProps(nextProps) {
if(this.props.num !== nextProps.num) {
this.setState({
childNum: nextProps.num
})
}
}
render() {
return (
This is number from a props
{this.props.numberPassed}
This is number from local state!
{this.state.childNum}
);
}
}
export {BtwComponentApp};
Poniżej moje rozwiązanie. Będę wdzięczna za wszelkie uwagi i wskazówki
class App extends React.Component {
constructor() {
super()
this.state = {
input: 0,
number: 0
}
}
sendToChild() {
this.setState({
number: this.state.input
})
}
onChangedInput(event) {
this.setState({
input: Number(event.target.value)
})
}
render() {
return (
)
}
}
class SmallCounter extends React.Component {
constructor() {
super()
this.state = {
counter: 0
}
}
componentWillReceiveProps(nextProps) {
this.setState({
counter: nextProps.counter,
})
}
increment() {
this.setState({
counter: this.state.counter + 1,
})
}
decrement() {
this.setState({
counter: this.state.counter – 1,
})
}
render() {
return (
)
}
}
To co napisałaś wygląda jakby miało działać. Ale ja właśnie edytowałem ten artykuł — koniecznie zobacz pod koniec od „Aktualizacja 11.02.2019 r”!
Akurat robiłem ćwiczenie XD
Długo kminiłem, ale React Tutorial pomógł
https://reactjs.org/docs/lifting-state-up.html
https://pastebin.com/1ujwR51X
Rozwiązanie dla aktualnego ćwiczenia:
https://pastebin.com/1ujwR51X
Czy może mi ktoś wyjaśnić dlaczego to nie działa?: Chodzi o cudzyslowy oraz bind(this)
{this.state.forwardValue &&}
tylko to:
{this.state.forwardValue &&
czy może mi ktoś wyjaśnić dlaczego to nie działa: ?
oczywiscie jest wyzej , testowalem przyklad z przykladow podanych przez uzytkownikow blogoa ponizej w kom.:
this.handleClick = this.handleClick.bind(this);
this.handleChange = this.handleChange.bind(this);
{this.state.forwardValue &&}
tylko to bez cudzyslowow i z bind.this? gubie sie jaka forma jest poprawna, bo przyklady podaja ludzie ze dziala a u mnie niestety
{this.state.forwardValue && }
Ale co dokładnie nie działa? Wrzuć cały przykład na codepen albo gdzieś 🙂
Cześć – po aktualizacji ćwiczeń fajnie było by wyjaśnić w kilku słowach o co chodzi z getDerivedStateFromProps i jaka jest różnica między nim a componentDidUpdate. Chwile mi zajęło, zanim zobaczyłem różnicę 🙂 Dodatkowo nie jestem pewny jednej rzeczy. Jeżeli komponent dziecka może mieć swój stan, którego inicjalna wartość jest ustawiana przez props i aktualizacja miała by się odbywać przez getDerivedStateFromProps jak rozróżnić że props się tak naprawdę zmienił jeżeli 2 razy z rzędu ustawiany jest ten sam props?
Przykład: https://stackblitz.com/edit/typeofweb-lesson10
Kliknięcie na guzik „set” zmienia prop dziecka, które dzięki getDerivedStateFromProps zmienia swój stan. Jednak gdy guzikami „+” i „-” zmienię stan dziecka i kliknę jeszcze raz „set” bez miany wartości inputa, oczekiwał bym, że stan dziecka się zaktualizuje, a nie dzieje się tak, bo defakto wartość prop.value się nie zmieniła.
Jak coś takiego ugrać?
Co to jest getDerivedStateFromProps i jak go używać wyjaśniłem we wpisie, który jest podlinkowany tuż przed ćwiczeniem.
W ćwiczeniu chodzi o to, aby z tej metody nie korzystać. Zamiast tego, należy wynieść stan do rodzica, tak, jak napisałem w podpowiedzi do zadania.
Rozwiązanie z użyciem getDerivedStateFromProps będzie najprawdopodobniej przekombinowane i złe.
https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html – to rozwiązało mój problem – ref lub key 🙂
Napisz do mnie na https://discord.typeofweb.com to Ci wszystko wyjaśnię 🙂
Dzięki, ale wydaje mi się że już wiem o co chodzi 🙂 Dokładniejsze przestudiowanie cyklu życia komponentu dużo wyjaśniło (świetny diagram –
) . Wiem, że w zadaniu chodziło lifting state up, ale ciekawiło mnie dlaczego wycofali się ze starej metody 🙂
Dobry kursik. Dobre zadanko . Wymyśliłem takie coś ;>
https://pastebin.com/Q9yMTmXp
Pozdro!
Z użyciem lifting state up:
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = { number: 5 };
this.handleNumberChange = this.handleNumberChange.bind(this);
}
handleNumberChange(number) {
this.setState(() => {
return { number }
})
}
render() {
return (
);
}
}
class Child extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
const { onNumberChange } = this.props;
onNumberChange(e.target.value);
}
render() {
const { number } = this.props;
return (
Lifting state up
)
}
}
Czy mój komentarz został usunięty?
Nie, po prostu leżał w spamie 🙁
Jest problem z typem, gdy wpisuje liczbę do inputa to Child interpretuje tego propsa jako String’a i zamiast inkrementować konkatenuję.. Próbowałem PropTypes lecz CodePen chyba tego nie obsługuje, czy jest na to jakiś inny sposób?
Moje rozwiązanie:
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = { value: 0,
showChild : true};
}
_onChange(e) {
this.setState({ value: e.target.value});
}
_toggleChild(){
this.setState(state => ({ showChild: !this.state.showChild}));
}
render() {
return (
{this.state.showChild && };
);
}
}
class Child extends React.Component {
constructor(props) {
super(props);
this.state = {
value: this.props.value,
};
}
componentWillReceiveProps(nextProps){
this.setState(state => ({
value : nextProps.value,
}));
}
_incrementChild(){
this.setState(state => ({
value : this.state.value + 1,
}));
}
_decrementChild(){
this.setState(state => ({
value : this.state.value – 1,
}));
}
render() {
return
{this.state.value}
;
}
}
ReactDOM.render( , document.getElementById(„app”));
To nie wina Reacta tylko DOM —
value
zawsze jest stringiem 🙂 Musisz dokonać konwersji na liczbę, np:value: Number(e.target.value)
>
class ParentComponent extends React.Component {
constructor(props) {
super(props);
this.state = {value: 0, forward: 0};
this.changeValue = this.changeValue.bind(this);
this.onButtonClicked = this.onButtonClicked.bind(this);
}
changeValue(event) {
const value = event.target.value;
this.setState(() => ({value:value}))
}
onButtonClicked() {
this.setState(state => ({forward: state.value}));
}
render() {
return (
Input: Forward value
);
}
}
class ChildComponent extends React.Component {
constructor(props) {
super(props);
const value = Number(props.value);
this.state = {parent: value, value: value};
this.onButtonClicked = this.onButtonClicked.bind(this);
}
onButtonClicked(event) {
const name = event.target.name;
let value = 0
if (name == "inc") {
value = this.state.value + 1;
} else if(name == "dec") {
value = this.state.value - 1;
}
this.setState(() => ({value: value}))
}
static getDerivedStateFromProps(props, state) {
const parentValue = props.value;
return (parentValue == state.parent) ? null : {parent:parentValue, value: parentValue};
}
render() {
return (
Child: {this.state.value}
+
-
)
}
}
export default ParentComponent;
Do tej pory przykłady były dobrze opisane. Ten tutaj jest słaby i niewiele tłumaczy.
Mogę wyjaśnić, ale musiałbyś zadać jakieś pytanie 🙂 Czego konkretnie nie rozumiesz?
Porównując tą lekcję z poprzednimi i kolejnymi tutaj w przykładzie nie omawiacie nic :). Sama konstrukcja postu jest taka jakby ktoś nie miał na niego czasu.
Zupełnie inaczej to już wygląda w kolejnym wpisie nr 11 gdzie bardzo przystępnie krok po kroku omawiacie poszczególne komponenty. Sam kurs całościowo oceniam bardzo dobrze, ta jedna lekcja nr 10 moim zdaniem mogłaby być dokładniej opisana.
Możesz złożyć reklamację i poprosić o zwrot pieniędzy, które zapłaciłeś za ten kurs. Oh, wait…
oki .. myślałem że rozumiem React do póki nie zacząłem komunikować ze sobą komponentów XD
-> componentDidMount – pobiera dane, ustawia state i dopiero render() wie wszystko żeby wysłać {…} do gdzie wykonywana jest metoda { setState } ..
… taki przypadek
.. koniec końców mogę wysłać dane tylko w render .. gdzie NIE mogę tego robić 🙁 ..
index.js:1 Warning: Cannot update during an existing state transition (such as within `render`). Render methods should be a pure function of props and state.
in TabContent (at MainMenuItem.js:34) .. no i jestem w kropce .. gdzie albo jak to robić?
Nie bardzo rozumiem problem, dane przesyłasz jako props, po co chcesz wywoływać setState w renderze?
Comp1 wystamia metodę przez inną class statyczną Static.eventListener:
eventHandler = ( newState = {} ) => {
this.setState( newState );
}
(coś jak ma to miejsce w komunikacji parent-child)
i w
Comp2.render() {
Static.eventListener( { 'nowe dane' } ); // tu się pojawia problem
}
co ciekawe jak wprowadzę Comp2.render( Comp3 Static.eventListener( { 'nowe dane' } ); dam w componentDidMount() ) problemu już niema … za to kółeczko się zamyka bo dane muszą być zmieniane więc w Comp3 trzeba przenieść Static.eventListener .. do metody która się 'odświeża', pytanie można użyć do tego shouldComponentUpdate ?
Przykro mi, ale absolutnie niczego nie zrozumiałem.
W temacie Lifting State Up: czy dobrą praktyką jest przekazywanie do obiektów zagnieżdżonych scope’a parent’a np:
Powyższe rozwiązanie pozwala na bezpośredni dostęp do logiki parenta z child’a.
… po 10 latach siedzenia w ExtJS i budowaniu całkiem sporych rozwiązań jestem przyzwyczajony do swobodnego poruszania się po wszystkich instancjach obiektów. Tam naturalnym jest to, że parent posiada handler’y do wszystkich wszystkich obiektów zagnieżdżonych i w drugą stronę każdy child posiada wskaźnik na parent’a.
To bardzo zła praktyka.
dzięki na info, tylko czy możesz to jakoś krótko rozwinąć – co jest dobrą praktyką ?
W jaki sposób obiekty mogą z sobą „gadać” w bardziej elastyczny sposób.
Zakładam, że podany przykład React’a z przekazywaniem metody to jeden z najprostrzych sposobów.
… zapewne znajdę odpowiedź w kolejnych częściach kursu … 🙂
Tak!
https://typeofweb.com/komunikacja-pomiedzy-komponentami-w-react-js/
+ redux później
dzięki – jest światełko w tunelu 🙂
Jest tu ktoś jeszcze? Mam duży problem z 2 punktem i już sama nie wiem, gdzie szukać pomocy. Tutaj moje wypociny:
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
input: null,
forwardValue: null
}
this.handleIncrement = this.handleIncrement.bind(this);
this.handleDecrement = this.handleDecrement.bind(this);
this.handleClick = this.handleClick.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleIncrement() {
this.setState({input: this.state.input + 1})
}
handleDecrement() {
this.setState({input: this.state.input – 1})
}
handleChange(event) {
this.setState({input: event.target.value});
}
handleClick() {
this.setState({forwardValue: this.state.input});
}
render() {
return (
Propagate to child
{this.state.forwardValue && }
);
}
}
class Child extends React.Component{
render() {
return (
{this.props.number}
+
–
)
}
}
Przesyłam moje rozwiązanie ćwiczenia i proszę.o konstruktywną krytykę.
https://pastebin.com/Rhcn4hH4