Menu

Komponenty klasowe w React: stan, cykl życia, konwersja

Komponent klasowy to klasa JavaScript, która rozszerza React.Component i zwraca JSX z metody render. Poznaj this.props, this.state i setState, wiązanie handlerów zdarzeń, metody cyklu życia, PureComponent i to, jak przerobić klasę na komponent funkcyjny.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Komponent klasowy to klasa JavaScript, która rozszerza React.Component i zwraca JSX z metody render(). Odczytuje propsy z this.props, trzyma stan w this.state i aktualizuje go przez this.setState(). W nowym kodzie zastąpiły je komponenty funkcyjne z hookami, ale klasy nadal są w pełni obsługiwane, pojawiają się w wielu starszych projektach i są jedynym sposobem na napisanie granicy błędów.

Każdy licznik trzyma własny stan. Dodaj trzeci <Counter label="Plums" />, a też zacznie od 0.

Budowa komponentu klasowego

import { Component } from 'react';

class Welcome extends Component {
    static defaultProps = { name: 'stranger' };

    constructor(props) {
        super(props); // required before using this
        this.state = { visits: 1 };
    }

    render() {
        return <h1>Hello, {this.props.name}. Visit #{this.state.visits}</h1>;
    }
}
  • Klasa rozszerza Component (albo React.Component przy import React from 'react').
  • render() jest wymagane. Musi być czyste, jak ciało komponentu funkcyjnego: czyta this.props i this.state, zwraca JSX.
  • this.props zawiera propsy. Są tylko do odczytu.
  • Stan można ustawić w constructor (najpierw wywołaj super(props)) albo, co dziś częstsze, jako pole klasy: state = { visits: 1 };.
  • static defaultProps nadal działa dla klas w React 19 (usunięto je tylko dla komponentów funkcyjnych).

this.setState scala obiekty i może przyjąć funkcję aktualizującą

setState robi dwie rzeczy inaczej niż setter z useState. Scala przekazany obiekt z bieżącym stanem, więc aktualizujesz tylko klucze, które wymienisz. I jak każda aktualizacja stanu w React nie jest stosowany od razu, więc używaj formy z funkcją aktualizującą, gdy następna wartość zależy od poprzedniej.

"+3 with objects" dodaje jeden, bo wszystkie trzy wywołania czytają to samo this.state.likes. "+3 with updaters" dodaje trzy. "Move to Paris" zachowuje name i likes (scalanie), ale gubi zip, bo scalanie sięga tylko jednego poziomu. Napisz address: { ...this.state.address, city: 'Paris' }, żeby go zachować. Opcjonalny drugi argument setState uruchamia się po aktualizacji, a konsola pokazuje tam nowy adres.

Wiązanie handlerów zdarzeń

Metoda przekazana jako onClick={this.handleClick} jest później wywoływana jako zwykła funkcja, bez swojego obiektu, więc this w jej środku ma wartość undefined.

Pierwszy przycisk wypisuje TypeError o odczycie setState z undefined. Drugi działa, bo funkcja strzałkowa w polu klasy przechwytuje this w chwili tworzenia instancji. Starsza poprawka, wciąż częsta w starym kodzie, wiąże metodę w konstruktorze:

constructor(props) {
    super(props);
    this.state = { on: false };
    this.handleClick = this.handleClick.bind(this);
}

Działa też strzałka w linii, onClick={() => this.handleClick()}.

Metody cyklu życia

Klasy reagują na montowanie, aktualizację i odmontowanie przez metody o stałych nazwach. Te, które zobaczysz najczęściej:

  • componentDidMount(): po tym, jak pierwsze renderowanie jest w DOM. Uruchamiaj timery, subskrypcje, żądania.
  • componentDidUpdate(prevProps, prevState): po każdej aktualizacji. Zanim zaczniesz pracę, porównaj z poprzednimi wartościami, bo inaczej wpadniesz w pętlę.
  • componentWillUnmount(): przed usunięciem. Cofnij to, co uruchomił componentDidMount.
  • shouldComponentUpdate(nextProps, nextState): zwróć false, żeby pominąć renderowanie.

Strona o cyklu życia komponentu zawiera pełną listę, kolejność wykonania i komponent, który wypisuje każdą z metod.

Przerabianie klasy na komponent funkcyjny

Oto klasa, która używa wszystkich trzech popularnych metod cyklu życia. Tyka co sekundę, wypisuje log, gdy zmienia się jej prop label, i zatrzymuje timer przy usunięciu.

A to ten sam komponent jako funkcja. Każda metoda cyklu życia staje się częścią efektu, a powiązane setup i czyszczenie stoją obok siebie, zamiast w dwóch metodach.

Oba timery liczą tak samo i wypisują log przy każdej zmianie etykiety. Wersja funkcyjna wypisuje też log raz przy zamontowaniu, bo efekt uruchamia się także po pierwszym renderowaniu. Kroki każdej konwersji:

  1. this.props.x staje się zdestrukturyzowanym propsem, { x }.
  2. Każdy klucz this.state staje się osobnym useState. Pamiętaj, że setter zastępuje wartość, a nie scala.
  3. Metody stają się zwykłymi funkcjami wewnątrz komponentu. Nie trzeba niczego wiązać.
  4. componentDidMount, componentDidUpdate i componentWillUnmount stają się wywołaniami useEffect, pogrupowanymi według tego, co robią, a nie według tego, kiedy się uruchamiają.
  5. createRef staje się useRef, a static contextType z this.context staje się useContext albo use.

Granice błędów zostaw jako klasy; nie mają odpowiednika funkcyjnego.

PureComponent

Zwykły komponent klasowy renderuje się za każdym razem, gdy renderuje się jego rodzic. PureComponent najpierw porównuje nowe propsy i stan z poprzednimi, na jednym poziomie głębokości, i pomija renderowanie, gdy nic się nie zmieniło. To klasowy odpowiednik React.memo.

Kliknij przycisk: wypisuje się tylko PlainLabel rendered, bo PureLabel widzi ten sam text. Przekaż obu text={'hello ' + clicks}, a oba będą renderować się przy każdym kliknięciu.

Porównanie jest płytkie, więc prop, który przy każdym renderowaniu rodzica jest nowym obiektem albo funkcją (style={{ color: 'red' }}, onClick={() => ...}), je unieważnia. To ta sama pułapka co przy React.memo.

Co React 19 usunął z klas

Jeśli utrzymujesz stary kod klasowy, kilka wzorców nie działa już w React 19:

  • String refs (ref="input" z this.refs.input). Użyj createRef() albo ref callback.
  • Przestarzały kontekst (contextTypes, childContextTypes, getChildContext). Użyj createContext ze static contextType.
  • propTypes są ignorowane. Użyj TypeScriptu albo własnych sprawdzeń.
  • findDOMNode zniknął. Zamiast tego umieść ref na elemencie DOM.

Stare metody componentWillMount, componentWillReceiveProps i componentWillUpdate też są przestarzałe. Nadal się wykonują, zarówno pod starymi nazwami, jak i z przedrostkiem UNSAFE_ (UNSAFE_componentWillMount i tak dalej), ale są oznaczone jako przestarzałe, a StrictMode przed nimi ostrzega; przenieś ich kod do componentDidMount, componentDidUpdate albo getDerivedStateFromProps.

Najczęściej zadawane pytania

Czy komponenty klasowe są przestarzałe w React?

Nie. Nadal są obsługiwane w React 19 i dalej będą działać, ale dokumentacja Reacta zaleca dla nowego kodu komponenty funkcyjne z hookami. Do granic błędów nadal potrzebujesz klasy.

Czym różnią się komponenty klasowe od funkcyjnych?

Komponent klasowy trzyma stan w this.state, aktualizuje go przez this.setState i reaguje na cykl życia przez nazwane metody. Komponent funkcyjny do tych samych zadań używa hooków (useState, useEffect), nie potrzebuje this i jest krótszy.

Czy setState aktualizuje stan od razu?

Nie. this.setState planuje aktualizację, a React renderuje później, więc zaraz po wywołaniu this.state ma wciąż starą wartość. Przekaż funkcję, this.setState((state) => ...), gdy nowa wartość zależy od starej, albo odczytaj wynik w callbacku podanym jako drugi argument lub w componentDidUpdate.

Dlaczego this ma wartość undefined w moim handlerze zdarzenia?

Metoda klasy przekazana jako onClick={this.handleClick} jest wywoływana bez swojego obiektu, więc this ma wartość undefined. Zdefiniuj handler jako funkcję strzałkową w polu klasy (handleClick = () => { ... }) albo zwiąż go w konstruktorze.

Co to jest PureComponent?

Component, który pomija renderowanie, gdy jego propsy i stan są płytko równe poprzednim. To klasowa wersja React.memo.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ