Некоторое время назад React представил React Hooks. Начиная с версии 0.59, вы также можете использовать их в React Native.

Что такое React Hooks?

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

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

Кроме того, если вы повторно используете код, а не дублируете его, вы экономите время на рефакторинге и потенциально имеете меньше источников ошибок (исправить что-то в одном месте означает, что вам также придется исправить это во всех других местах).

Обзор жизненного цикла компонента React

Я не хочу углубляться в жизненный цикл, поскольку у React уже есть отличная документация по этому поводу. Вы должны знать несколько важных вещей: рендеринг, монтаж компонентов, конструктор, а также свойства и состояние. Для хорошего обзора жизненного цикла вы можете взглянуть на следующее:

Это самые важные вещи, которые вы должны знать о компоненте React и его жизненном цикле:

Реквизит

Свойства - это входные данные компонента, поэтому вы добавляете их в компонент при его создании. Реквизиты по определению не могут измениться, но вы можете добавить к реквизитам функцию, которая сделает это за вас (может сбивать с толку).

Состояние

Состояние - это то, что может динамически изменяться (например, ввод текста) и всегда связано с чем-то (например, с компонентом). Вы можете изменить состояние, используя функцию 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 здесь, если вы не знаете, зачем вам bind определенные методы. This также имеет несколько ценных примеров кода.

Компонент смонтирован и будет отключен

Метод жизненного цикла componentDidMount вызывается только один раз после первого рендеринга компонента. Например, это может быть место, где вы выполняете запросы или регистрируете прослушиватели событий.

Кроме того, перед «уничтожением» компонента вызывается метод жизненного цикла componentWillUnmount. Это должно быть место, где вы отменяете в конечном итоге выполняющиеся запросы (чтобы они не пытались изменить состояние размонтированного компонента или чего-то еще), а также отмените регистрацию любого используемого вами прослушивателя событий. Наконец, это предотвратит утечку памяти в вашем приложении (память, которая не используется, не освобождается).

Проблема, с которой, вероятно, столкнулись многие (и я тоже), была именно той, которую я описал в последнем абзаце. Если вы используете функцию Window setTimeout для выполнения некоторого кода с задержкой, вам следует позаботиться об использовании clearTimeout для отмены этого таймера, если компонент отключается.

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

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

Разница между Component и PureComponent: возможно, вы уже слышали о PureComponent от React. Чтобы понять разницу, вам нужно знать, что shouldComponentUpdate(nextProps, nextState) используется / вызывается, чтобы определить, должно ли изменение свойств и состояния запускать повторный рендеринг компонента. Нормальный React.Component всегда выполняет повторный рендеринг при любом изменении (поэтому он всегда возвращает истину). React.PureComponent выполняет поверхностное сравнение свойств и состояния, поэтому он выполняет повторный рендеринг только в том случае, если какие-либо из них изменились. Имейте в виду, что если вы измените глубоко вложенные объекты (вы измените их), поверхностное сравнение может не обнаружить этого.

Если вы спросите себя, какое место занимает крючок в этом жизненном цикле, ответ будет довольно простым. Один из самых важных хуков - useEffect. Вы передаете функцию useEffect, которая запускается после вызова рендеринга. По сути, он равен componentDidUpdate. Если вы вернете функцию из переданной функции useEffect, вы сможете обработать componentWillUnmount код. Поскольку useEffect запускается после каждого рендеринга (что не всегда имеет смысл), вы можете ограничить его приближением к componentDidMount и componentWillUnmount, передав [] в качестве второго аргумента. Это сообщает React, что этот useEffect должен вызываться только при изменении определенного состояния (в данном случае [], что означает только один раз).

Самый интересный крючок - useState. Его использование довольно простое: вы передаете начальное состояние и получаете взамен пару значений (массив), где первый элемент - это текущее состояние, а второй - функция, которая его обновляет (например, setState()). Если вы хотите узнать больше о хуках, загляните в документацию React.

Наконец, я хочу представить простой пример компонента React Native с React Hooks. Он содержит компонент View с текстом и кнопкой. Нажимая кнопку, вы увеличиваете счетчик на 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 г.