Преди известно време React представи React Hooks. От версия 0.59 можете също да ги използвате в React Native.

Какво представляват React Hooks?

React Hooks са начин за използване на функции със състояние във функционален компонент. Функционалните компоненти са компоненти, написани като функция, така че те приемат някои входни данни (реквизити) и връщат React Element. Преди React Hooks ще трябва да напишете JSX (JavaScript и XML) клас, който се разширява от React Component, за да получите достъп до код, свързан със състояние или жизнен цикъл.

И така накратко: с React Hooks можете да пишете по-прост код, да използвате повторно код (функции) и да направите всичко по-лесно за поддръжка и тестване. В крайна сметка, колкото повече време отделите, за да направите кода си поддържаем, толкова по-малко време ще загуби някой друг, опитвайки се да разбере кода ви.

Освен това, ако използвате повторно код, вместо да имате дубликати, вие спестявате време за рефакторинг и имате потенциално по-малко източници на грешки (коригирането на нещо на едно място означава, че трябва да го коригирате и на всички други места).

Пробив в жизнения цикъл на React компонент

Не искам да навлизам дълбоко в жизнения цикъл, тъй като React вече има „страхотна документация“ за това. Има някои важни неща, които трябва да знаете: изобразяване, монтиране на компоненти, конструктор, както и подпори и състояние. За добър преглед на жизнения цикъл можете да разгледате следното:

Това са най-важните неща, които трябва да знаете за React Component и неговия жизнен цикъл:

Реквизит

Реквизитите са вход на компонент, така че това е нещо, което поставяте в компонент, когато го създавате. Реквизитите по дефиниция не могат да се променят, но можете да добавите функция към реквизитите, които правят това вместо вас (може да е объркващо).

състояние

Състояние е нещо, което може да се променя динамично (като въвеждане на текст) и винаги е обвързано с нещо (например компонент). Можете да промените състоянието, като използвате функцията setState(), която само уведомява компонента за промяна на състоянието. Разгледайте следния пример и често срещан капан с React и setState():

// not so good
console.log(this.state.test); // 5
this.setState({ test: 12 });
console.log(this.state.test); // might be 5 or 12
// good
this.setState({ test: 42 }, () => { console.log(this.state.test); // 42 });

Конструктор

Коснструкторът не винаги е необходим. Има обаче някои случаи на употреба: инициализиране на състояние и методи за обвързване към this. Това, което определено не трябва да правите там, е да извиквате дълго работещи методи, тъй като това може да забави първоначалното ви изобразяване (вижте диаграмата по-горе). Така че общ компонент и конструктор могат да изглеждат така:

class MyComponents extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      test: 42
    };
    this.renderSomeText = this.renderSomeText.bind(this);
  }
  // you could also do this, so no constructor needed
  state = {
    test: 42,
  }
  renderSomeText() {
    return <Text>this.state.test</Text>
  }
}

Ако не обвържете метод в конструктора и само инициализирате състоянието, дори нямате нужда от конструктор (запазване на код). Вижте моята статия за React Performance тук, ако не знаете защо трябва да bind определени методи. „Това“ също има някои ценни примери за код.

Компонентът се монтира и ще се демонтира

Методът на жизнения цикъл на componentDidMount се извиква само веднъж, след като компонентът е бил изобразен за първи път. Това може да е мястото, където правите заявки или регистрирате слушатели на събития, например.

Освен това, методът на жизнения цикъл на componentWillUnmount се извиква преди компонентът да бъде „унищожен“. Това трябва да е мястото, където можете да отмените евентуално стартирани заявки (така че да не се опитват да променят състоянието на немонтиран компонент или нещо подобно), както и да дерегистрирате всеки слушател на събития, който използвате. И накрая, ще ви предпази от изтичане на памет в приложението ви (паметта, която не се използва, не се освобождава).

Проблем, с който вероятно са се сблъскали мнозина (и аз също) беше точно това, което описах в последния параграф. Ако използвате функцията Window setTimeout, за да изпълните някакъв код по забавен начин, трябва да се погрижите да използвате clearTimeout, за да отмените този таймер, ако компонентът се демонтира.

Други методи на жизнения цикъл

componentWillReceiveProps(nextProps) или от React версия 16.3 getDerivedStateFromProps(props, state)методът на жизнения цикъл се използва за промяна на състоянието на компонент, когато неговите реквизити се променят. Тъй като това е по-сложна тема и вероятно я използвате (и трябва да я използвате) рядко, можете да прочетете за нея тук.

Разлика между Component и PureComponent: Може би вече сте чували за PureComponent на React. За да разберете разликата му, трябва да знаете, че shouldComponentUpdate(nextProps, nextState) се използва/извиква, за да определи дали промяната в подпорите и състоянието трябва да задейства повторно изобразяване на компонента. Нормалният React.Component винаги се изобразява отново при всяка промяна (така че винаги връща true). React.PureComponent прави плитко сравнение на подпори и състояние, така че рендерира само ако някой от тях е променен. Имайте предвид, че ако промените дълбоко вложени обекти (мутирате ги), плиткото сравнение може да не го открие.

Ако се запитате къде куките се вписват в този жизнен цикъл, отговорът е доста лесен. Една от най-важните кукички е useEffect. Предавате функция на useEffect, която ще се изпълнява след извикването за рендиране. Така че по същество той е равен на componentDidUpdate. Ако върнете функция от предадената функция на useEffect, можете да обработвате кода componentWillUnmount. Тъй като useEffect се изпълнява след всяко изобразяване (което може да не винаги има смисъл), можете да го ограничите до по-близо до componentDidMount и componentWillUnmount, като подадете [] като втори аргумент. Това казва на React, че този useEffect трябва да се извиква само когато определено състояние се е променило (в този случай [], което означава само веднъж).

Най-интересната кука е useState. Използването му е доста просто: предавате първоначално състояние и получавате двойка стойности (масив) в замяна, където първият елемент е текущото състояние, а вторият е функция, която го актуализира (като setState()). Ако искате да прочетете повече за куките, вижте документацията на React.

И накрая, искам да представя прост пример за React Native компонент с React Hooks. Той съдържа изглед с компонент текст и бутон. Като щракнете върху бутона, вие увеличавате брояча с 1. Ако броячът достигне стойност 42 или по-голяма, той остава на 42. Можете да спорите дали има смисъл или не. Особено след като стойността скоро ще бъде увеличена до 43, след което ще се изобрази веднъж, след което useEffect ще я върне обратно на 42.

import React, { useState, useEffect } from 'react';
import { View, Text, Button } from 'react-native';
export const Example = () => {
  const [foo, setFoo] = useState(30);
  useEffect(() => {
    if (foo >= 42) {
      setFoo(42);
    }
  }, [foo])
  return (
    <View>
      <Text>Foo is {foo}.</Text>
      <Button onPress={() => setFoo(foo + 1)} title='Increase Foo!' />
    </View>
  )
}

React Hooks са чудесен начин да напишете още по-чисти React компоненти. Естествената му способност да създава код за многократна употреба (можете да комбинирате куките си) го прави още по-голям. Фактът, че страничните ефекти от почистването (абонаменти, заявки) се случват за всеки рендер по подразбиране, помага да се избегнат грешки (може да забравите да се отпишете), както е посочено тук.

Първоначално публикувано в https://mariusreimer.com на 1 май 2019 г.