05. React - State a Lifecycle

13.02.2019 Programování #react #learning

Úvod do konceptu stat a lifecycle komponet v Reactu.


Použijeme příklad z předchozích lekcí, tikajících se tikajících hodin, ve kterém jsme se naučili pouze jeden způsob, jak aktualizovat uživatelské rozhraní, pomocí ReactDOM.render() pro vykreslovaní výstupu:

GitHub

V této části se dozvíme, jak vyrobit komponentu Clock, která bude opakovaně použitelná a zapouzdřená. Nastaví vlastní časovač a aktualizuje se každou sekundu.

Začneme zapouzdřením, jak hodiny vypadají:

function Clock(props) {
  return (
    <div>
      <h1>Hello, world!</h1>
      <h2>It is {props.date.toLocaleTimeString()}.</h2>
    </div>
  );
}

function tick() {
  ReactDOM.render(
    <Clock date={new Date()} />,
    document.getElementById('root')
  );
}

setInterval(tick, 1000);

Nicméně chybí zásadní požadavek: skutečnost, že Clock nastaví časovač a aktualizuje uživatelské rozhraní každou sekundu, by měl být záležitostí implementace Clock.

V ideálním případě bychom to chtěli jednou napsat a mít vlastní Clock aktualizaci:

ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);

K tomu je třeba přidat do komponenty Clock "state".

State je podobný props, ale je soukromý a plně kontrolovaný komponentou.

Již dříve jsme zmínili, že komponenty definované jako třídy mají některé další funkce. Lokální state je přesně: funkce dostupná pouze pro třídy.

Převedení funkce na třídu

Funkci komponenty lze převést jako třídu Clock v pěti krocích:

  1. Vytvořte třídu ES6 se stejným názvem, která rozšiřuje třídu React.Component.
  2. Přidejte do ní jednu prázdnou metodu render().
  3. Přesuňte tělo funkce do render() metody.
  4. Změňte props za this.props do těla render().
  5. Vymažte prázdnou deklaraci funkce.
class Clock extends React.Component {
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.props.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

Clock je nyní definována spíše jako třída než jako funkce.

Metoda render bude volána pokaždé, když nastane aktualizace, ale pokud budeme renderovat < Clock / > do stejného DOM uzlu bude použita pouze jediná instance třídy Clock. To umožňuje používat další funkce, jako jsou lokální state a metody životního cyklu.

Přidání Local State a Class

Přesuneme date z props do state ve třech krocích:

1. Změňte this.props.date za this.state.date v metodě render():

class Clock extends React.Component {
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

2. Přidejte konstruktor pro inicializaci this.state:

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

Všimněte si jak props přistupuje k základnímu konstruktoru:

constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

Třída komponenty by měla vždy volat základní konstruktor pomocí props.

3. Vymažte date prop z elementu < Clock / >:

ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);

Později přidáme kód časovače zpět k samotné komponentě,

výsledek vypadá následovně:

GitHub

Následně vytvoříme Clock pro nastavení aktuálního času a jeho automatické změny.

Přidání Lifecycle metod a třídy

V aplikacích s mnoha komponentami je velmi důležité uvolnit zdroje komponent, které jsou zničeny.

Chceme nastavit časovač, kdykoli Clock poprvé vykreslíme do DOM. Toto se nazývá v Reactu "mouting".

Také bychom chtěli vymazat tento časovač, kdykoli se DOM Clock vymaže. Toto se v reakci nazývá "unmounting".

Můžeme deklarovat speciální metody na třídě komponent, aby při spuštění a odpojování součásti spustili nějaký kód:

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  componentDidMount() {

  }

  componentWillUnmount() {

  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

Tyto metody se nazývají "lifecycle metody".

Metoda componentDidMount() se vol8 á po vykreslení DOM, tedy po výstupu komponenty. Takže to je to správné místo pro nastavení časovače:

componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

Všimněte si, že dimerID je uloženo s this.

Zatímco je this.props v Reactu nastaven sám sobě, this.state má zvláštní význam. Můžete volně přidat další pole do třídy,jestliže potřebujete uložit něco co se netýká toku dat.

Zrušení časovače se provádí pomocí metody componentWillUnmount():

omponentWillUnmount() {
    clearInterval(this.timerID);
  }

Nakonec implementujeme metodu tick(), pro aktualizace Clock každou sekundu.

Použije se this.setState() k naplánování aktualizací lokálního state komponent:

GitHub

Nyní se hodiny mění každou sekundu.

Rychlá rekapitulace toho co se děje v jakých metodách, které jsou volány:

  1. Při odeslání   do ReactDOM.render()reaguje konstruktor komponenty Clock. Protože Clock potřebuje zobrazit aktuální čas, inicializuje this.state s objektem včetně aktuálního času. Tento stav později aktualizujeme.
  2. React pak volá metodu komponety Clock render(). Takto se React dozví, co se má zobrazit na obrazovce. React poté aktualizuje DOM tak, aby odpovídala výstupu Clock.
  3. Když je výstup Clock vložen do DOM, React zavolá metodu componentDidMount() životního cyklu. Uvnitř komponenty Clock se žádá prohlížeč aby nastavil časovač, který volá metodu komponenty tick() jednou za sekundu.
  4. Každou sekundu prohlížeč volá metodu tick(). Uvnitř komponenty Clock naplánuje aktualizaci uživatelského rozhraní voláním setState() s objektem obsahujícím aktuální čas. Díky setState() React ví, že stav se změnil a render() znovu zavolá metodu, aby zjistil, co má být na obrazovce. Tentokrát se this.state.daterender() metodě bude lišit a tak výstup renderu bude obsahovat aktualizovaný čas. Služba React aktualizuje DOM odpovídajícím způsobem.
  5. Pokud je součást Clock někdy odebrána z DOM, React zavolá componentWillUnmount() metodu životního cyklu tak, aby byl časovač zastaven.

Jak správně používat state

Existují tři věci, které bystě měli při práci se setState() vědět.

Nemodifikujte přímo state

Například nerenderujte komponentu:

// Wrong
this.state.comment = 'Hello';

Použijte radši setState():

// Correct
this.setState({comment: 'Hello'});

Jediným místem, kde můžete použít this.state je konstruktor.

Aktualizace state mohou být asynchronní

React může volat setState() vícekrát pro jednu aktualizaci.

Protože this.propsthis.state mohou být aktualizovány asynchronně, něměli bychom spoléhat na jejich hodnoty pro výpočet dalšího stavu.

Například tento kód nemusí aktualizovat počítadlo:

// Wrong
this.setState({
  counter: this.state.counter + this.props.increment,
});

Pokud vyše uvedený kód chceme upravit, použijeme druhou podobu setState(), která přijímá funkci dříve než objekt. Tato funkce obdrží předchozí stav jako první argument a props v době použití jako druhý argument:

// Correct
this.setState((state, props) => ({
  counter: state.counter + props.increment
}));

Použili jsme funkci šipky, ale pracujeme také s běžnými funkcemi:

// Correct
this.setState(function(state, props) {
  return {
    counter: state.counter + props.increment
  };
});

State aktualizace jsou slučovány

Když voláme setState(), React sloučí objekty do aktuálního stavu.

Například state může obsahovat několik nezávislých proměnných:

 constructor(props) {
    super(props);
    this.state = {
      posts: [],
      comments: []
    };
  }

Potom je lze aktualizovat nezávisle, pomocí volání setState().

  componentDidMount() {
    fetchPosts().then(response => {
      this.setState({
        posts: response.posts
      });
    });

    fetchComments().then(response => {
      this.setState({
        comments: response.comments
      });
    });
  }

Průchod z hora dolů

Ani rodiče, ani podřízené komponenty nemohou vědět, zda je určitá součást state nebo bez state, a neměli by se starat o to, zda je definována jako funkce nebo třída.

To je důvod, proč se state často nazývá lokální nebo zapouzdřený. Není přístupná žádné jiné komponentě, než té, která jej vlastní a nastavuje.

Komponenta se může rozhodnout, že svůj state předá dolů jako props do svých podřízených komponent:

<h2>It is {this.state.date.toLocaleTimeString()}.</h2>

To také funguje pro uživatelem definované komponenty:

<FormattedDate date={this.state.date} />

Komponenta FormattedDate by obdržela date ve svých props a nebude vědět, zda to přišlo z Clock‚ se state, z Clock s props, nebo bylo napsáno ručně:

function FormattedDate(props) {
  return <h2>It is {props.date.toLocaleTimeString()}.</h2>;
}

To se běžně nazývá tok dat "shora dolů" nebo "jednosměrný". Každý state je vždy vlastněn určitou konkrétní komponentou a všechna data nebo uživatelské rozhraní odvozené od tohoto state mohou ovlivňovat pouze komponenty "pod" ve stromu.

Pokud si představíte strom komponent jako vodopád props, každý state komponenty je jako další zdroj vody, který se připojí k libovolnému bodu, ale také klesá.

Abychom ukázali, že všechny komponenty jsou skutečně izolované, můžeme vytvořit komponentu App, která vykreslí tři <Clock>:

GitHub

Každý Clock nastaví vlastní časovač a aktualizuje se nezávisle.

V aplikacích React, zda je komponenta se state nebo bez state, se považuje za podrobnosti implementaci součásti, která se může časem měnit. Ve state komponentách můžete použít komponentu bez state a naopak.