Why React Hooks

React.js

            Why React Hooks

Введение

1.1 Зачем использовать SFC (функциональный компонент без сохранения состояния) в первую очередь

Stateless Function Component:

const App = (props) => (
  <div>Hello, {props.name}</div>
)

Class Component:

class App extends React.Component {
  render() {
    return (
      <div>Hello, {this.props.name}</div>
    )
  }
}

Выше приведено сравнение двух простейших функциональных компонентов и компонентов класса, Прежде всего, с точки зрения количества строк, 3

Посмотрите на код после компиляции babel в es2015:

Function Component:

"use strict";

var App = function App(props) {
  return React.createElement("div", null, "Hello, ", props.name);
};

Class Component:

Удалена куча вспомогательных функций Babel.

"use strict";

var App =
/*#__PURE__*/
function (_React$Component) {
  _inherits(App, _React$Component);

  function App() {
    _classCallCheck(this, App);

    return _possibleConstructorReturn(this, _getPrototypeOf(App).apply(this, arguments));
  }

  _createClass(App, [{
    key: "render",
    value: function render() {
      return React.createElement("div", null, "Hello, ", this.props.name);
    }
  }]);

  return App;
}(React.Component);

Функциональный компонент — это обычная функция JS, компонент класса не поддерживается ES2015.classПричина в том, что будет скомпилировано много кода, связанного с классами.

В то же время из-за специфики функционального компонента нижний слой React может выполнятьДополнительные оптимизации производительности.

В общем, следующие моменты:

  • Легче читать и одиночный тест
  • Пишите меньше кода, компилируйте более компактный код
  • Команда React может оптимизировать производительность этого компонента.

1.2 Раздражаетbind(this)

В React Class Component мы, должно быть, написали много кода, подобного этому.

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
    	name: 'rccoder',
    	age: 22
    },
    this.updateName = this.updateName.bind(this);
    this.updateAge = this.updateAge.bind(this);
  }
  
  render() {
    <div onClick={this.updateName}	
</div>
  }
}

Конечно, проблема не в React, а в JavaScript, который указывает на проблему, просто взгляните на этот код:

class Animate {
  constructor(name) {
    this.name = name;
  }
  getName() {
    console.log(this);
    console.log(this.name)
  }
}

const T = new Animate('cat');
T.getName();  // `this` is Animate Instance called Cat

var P = T.getName;
P(); // `this` is undefined

Этот пример точно такой же, как React выше.Запись this без привязки приведет к тому, что это будет глобальным this, который не определен.

Лучший способ решить эту проблему - связать это внутри конструктора.

В новой версии ES естьPublic Class Fields SyntaxЭта проблема может быть решена путем:

class Animate {
  constructor(name) {
    this.name = name;
  }
  getName = () => {
    console.log(this);
    console.log(this.name)
  }
}

const T = new Animate('cat');
T.getName();  // `this` is Animate Instance called Cat

var P = T.getName;
P(); // `this` is Animate Instance called Cat

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

Несмотря на то, что синтаксис открытых полей класса едва ли может решить эту проблему с помощью функции стрелки, проблема, на которую указывает this, по-прежнему вызывает у людей «панику».

1.2 Брошенный через несколько жизней

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

  • componentWillMount
  • componentWillRecieveProps
  • componentWillUpdate

недавно представленный

  • getDerivedStateFromProps
  • getSnapshotBeforeUpdate

getDerivedStateFromPropsа такжеgetSnapshotBeforeUpdateОба возвращают обработанный объект вcomponentDidUpdate, вся логика, которая должна работать, помещается вcomponentDidUpdateв.

В общем:

  • getDerivedStateFromProps + componentDidUpdateможно заменитьcomponentWillReceivePropsвсе обычные функции;
  • getSnapshotBeforeUpdate + componentDidUpdateможно заменитьcomponentWillUpdateвсе функции.

специфическийпричинаа такжеРуководство по миграцииВы можете обратиться к официальному блогу React:Update on Async Rendering, есть более подробное пошаговое руководство.

В конце концов, вы все еще должны чувствовать то же самое, что компонент класса имеет так много жизненных циклов и кажется таким сложным.


Разговоры о куче тем, которые, кажется, не имеют ничего общего с темой выше, на самом деле предназначены для того, чтобы вы почувствовали, что «Функциональный компонент — это хорошо», а затем с удовольствием прочитали следующее.


2. Что такое React Hooks

Наконец-то добрались до части, связанной с хуками, сначала посмотрим, что естьHooks:

2.1 Что такое хуки

Во-первых, давайте взглянем на всем известный WebHook:

Webhooks allow you to build or set up GitHub Apps which subscribe to certain events on GitHub.com. When one of those events is triggered, we'll send a HTTP POST payload to the webhook's configured URL. Webhooks can be used to update an external issue tracker, trigger CI builds, update a backup mirror, or even deploy to your production server. You're only limited by your imagination.

- Введение в GitHub WebHooks

Суть в следующем: когда одно из этих событий инициируется, мы отправляем полезную нагрузку HTTP POST на настроенный URL-адрес веб-перехватчика.

2.2 Что такое хуки React

Так что же такое React Hooks?

Hooks are a new addition in React 16.8. They let you use state and other React features without writing a class.

Вроде бы отличие от WebHook не маленькое, но на самом деле оно не маленькое, как это объяснить?

React Hook открывает некоторые хуки функционального компонента.Через некоторые встроенные хуки функциональный компонент может иметь свое собственное состояние и жизненный цикл, и им можно манипулировать в хуке. Кроме того, комбинируя встроенный хук + вашу собственную бизнес-логику, можно сгенерировать новый пользовательский хук, чтобы упростить повторное использование некоторой бизнес-логики.

3. Некоторые проблемы перед хуками

3.1 Проблема повторного использования логики между разными компонентами

Часто ожидается, что компоненты с разными представлениями будут иметь одну и ту же логику, например: принудительный вход в систему, вставка некоторых значений и т. д. В настоящее время мы часто используем HOC и renderProps для обертывания этого слоя логики.В общем, слой оболочки будет добавлен для изменения уровня компонентов.По мере увеличения сложности бизнес-логики будет возникать проблема ада оболочки.

3.2 Сложное чтение сложных компонентов

По мере увеличения сложности бизнес-логики наши компоненты часто выполняют множество действий в жизненном цикле, например запрашивают данные в componentDidMount, отправляют скрытые точки и т. д. Короче говоря, за один жизненный цикл будет написано несколько совершенно не связанных друг с другом кодов, что приведет к незаметному увеличению различных затрат.

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

3.3 Некоторые проблемы, с которыми сталкивается компонент класса

С человеческой точки зрения, компоненты класса должны быть обеспокоены этим указанием и т. д. Большинство из них часто путаются в том, использовать ли компоненты функции или компоненты класса, с точки зрения машины объем компиляции компонентов класса слишком велик. большая и горячая перезагрузка нестабильна.

4. Каковы функции хуков

На три вопроса, упомянутых выше, Хуки в некотором смысле ответили, как они ответили на них?

В настоящее время хуками являются: хук состояния, хук эффекта, хук контекста и пользовательский хук.

4.1 State Hook

import React, { useState } from 'react';

function Example() {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

useStateЯвляется API для State Hook. ПотреблениеinitialState, который возвращает массив, где первое значение — это состояние, а второе значение — функция, изменяющая состояние.

еслиinitialStateУлучшение должно потреблять много вычислительной мощности, и не ожидается, что эти вычисления заблокируют работу, которую нужно будет выполнить позже.initialStateМожет быть функцией, которая будет вызываться перед рендерингом для достижения эффекта Lazy Calc.

useState(() => {
  // ... Calc
  return initialState;
})

В то же время, чтобы избежать ненужных накладных расходов на производительность, при установке State, если два значения равны, ререндеринг не сработает. (Чтобы судить о том, что два значения равны, используйтеObject.is)

4.2 Effect Hook

import React, { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  // Similar to componentDidMount and componentDidUpdate:
  useEffect(() => {
    // Update the document title using the browser API
    document.title = `You clicked ${count} times`;
    return () => {
      ... // Similar to componentWillUnMount。Named as clear up effect
    }

  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

useEffectЭквивалентно классу КомпонентcomponentDidMount,componentDidUpdate,componentWillUnmountБольшой синтез трех жизненных циклов, функции эффекта будут выполняться при монтировании, обновлении и выгрузке компонента. Это лучше всего понять с «после рендеринга».

Стоит отметить, что контент в Effect Hook будет не таким, какcomponentDidMount,componentDidUpdateТо же, что блокировка рендеринга. Если вы не ожидаете такого поведения, но используете API, чтобы вести себя так жеuseLayoutEffect. (Общие вопросы быстрого добавления калькулятора)

В Function Component, как и в useState, можно многократно использовать useEffect, чтобы при организации бизнес-логики можно было разделить фрагменты кода по бизнес-логике (вместо Class Component можно разделять фрагменты кода только по жизненному циклу).

Выполнение хука эффекта на самом деле происходит «после рендеринга», чтобы избежать выполнения всех хуков эффектов для каждого рендера.useEffectПредоставляется второй входной параметр (который представляет собой массив). Эффектный хук будет выполняться только после изменения значения в массиве после рендеринга компонента. Если передан пустой массив, он будет выполняться только после первого монтирования и до Размонтирование компонента. Этот уровень оптимизации теоретически может быть выполнен во время компиляции, и команда React может удалить этот уровень позже.

4.3 Custom Hook

import React, { useState, useEffect } from 'react';

function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);

  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }

  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
    };
  });

  return isOnline;
}

function FriendListItem(props) {
  const isOnline = useFriendStatus(props.friend.id);

  return (
    <li style={{ color: isOnline ? 'green' : 'black' }}>
      {props.friend.name}
    </li>
  );
}

useFriendStatusЭто типичный пользовательский хук, который использует useState и useEffect для инкапсуляции подписанного списка друзей, установки состояния друга, отмены подписки при удалении компонента и, наконец, возврата состояния, указывающего, находится ли он в сети или нет. можно использовать как встроенный хук.Используйте то же самое, наслаждайтесь инкапсулированной логикой серии.

Внутри хуков, даже в функциональном компоненте, каждый вызов хуков имеет собственное изолированное пространство, чтобы гарантировать, что разные вызовы не мешают друг другу.

useFriendStatusкuseНачало — это условность React Hooks, которую удобно идентифицировать как Hook, и плагин eslint также распознает это написание, чтобы избежать лишних неприятностей.

В то же время, еслиuseXXXизinitialStateявляется переменной, и когда эта переменная изменится, хук автоматически отменит подписку на предыдущее сообщение и перемонтирует хуки. То есть в приведенном выше примере, еслиprops.friend.idЕсли изменение произойдет, хуки useFriendStatus будут перемонтированы, а онлайн-статус будет отображаться нормально.

4.4 Context Hook

function Example() {
  const locale = useContext(LocaleContext);
  const theme = useContext(ThemeContext);
  // ...
}

Входным параметром useContext является контекст, предоставленный Provider.Если контекст изменится, возвращаемое значение также изменится немедленно.

4.5 Reduce Hook

const initialState = {count: 0};

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}

function Counter({initialState}) {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
    </>
  );
}

Если изменение состояния имеет более сложный поток состояния, вы можете использовать useReducer, чтобы сделать его более похожим на Redux, чтобы сделать этот уровень логики более понятным.В то же время Reduce Hook также предоставляет функцию Lazy Calc., вы можете использовать его, когда вам это нужно.

Кроме того, встроенные хуки также имеютuseCallback,useMemo,useRef,useImperativeHandle,useLayoutEffect,useDebugValue. можешь идтиздесьУзнайте больше об использовании.

Пять, пример сравнения

Этот пример — пример Дэна на React Conf, очень показательный:

Адрес видео:React Today and Tomorrow and 90% Cleaner React With Hooks

В то же время, вот несколько хуков, взгляд на реализацию и решенные проблемы может дать более глубокое понимание прелести хуков:react hooks codesandbox

6. Когда будет использоваться React Hook?

  • Предпочитаю писать функциональный компонент
  • Хотите использовать функциональный компонент с состоянием и жизненным циклом
  • Не нравится разделение блоков кода в соответствии с жизненным циклом в компоненте класса (вместо разделения блоков кода в соответствии с бизнес-логикой)
  • Хотите усовершенствовать общую бизнес-логику различных компонентов пользовательского интерфейса, не попадая в ад оберток из-за HOC или renderProps.

Семь, введение проблемы

7.1 Странный эффект использования

useEffect может переопределить три операции жизненного цикла componentDidMount, ComponentDidUpdate и componentWillUnmount, но в некотором смысле реализация операции componentWillUnMount немного удушает.Если вы человек, который не читал документацию, вы никогда не узнаете, как это сделать . . .

useEffect(() => {
  // cDM or cDU
  return () => {
    // cWU
  }
})

7.2 Базовая реализация вызывает логические проблемы

Внутренняя реализация React Hook заключается в использовании xxx, потому что есть два ограничения на использование React Hook.

  • Хуки можно вызывать только на верхнем уровне, а не в циклах, условиях оценки и вложенных функциях.Это гарантирует, что хуки вызываются последовательно каждый раз, иначе значение, полученное из кортежа, может не совпадать со значением в нужном вам кортеже, и то же самое верно для эффекта. Конкретная причина, вероятно, заключается в том, что очередь поддерживается внутри, чтобы представлять порядок, в котором выполняются хуки, и порядок определяется, когда порядок формально определен.Если он больше не находится на верхнем уровне, это может привести к несоответствиям между порядок и время выполнения хуков, приводящих к проблемам.Подробности см. в официальном объяснении React:explanation
  • Только функциональный компонент и пользовательские хуки могут вызывать хуки React, обычные функции не могут вызывать хуки.

Команда React добавила для этого плагин eslint:eslint-plugin-react-hooks, это гибкий способ решения проблемы.

8. Часто задаваемые вопросы

8.1 Почему useState возвращает массив

Массив здесь, в некотором смысле, называетсяtupleПо контейнеру будет понятнее, но, к сожалению, в JavaScript такого понятия нет.

Кроме того, что, если он возвращает объект?

let { state: name, setState: setName } = useState('Name');
let { state: surname, setState: setSurname } = useState('Surname');

Выглядит как хуже.

8.2 Проблемы с производительностью

shouldComponentUpdateиспользоватьuseMemoВот и все, см.:How to memoize calculations?.

В то же время метод написания хуков позволяет избежать накладных расходов на создание большого количества примеров и событий привязки при создании компонента класса.Кроме того, метод написания хуков позволяет избежать глубокой вложенности HOC или renderProps, что легче для React иметь дело.

8.3 Можно ли охватить все жизненные циклы компонента класса?

getSnapshotBeforeUpdateа такжеcomponentDidCatchВ настоящее время не распространяется

8.4 Как получить prevState

Получить с помощью useRef

Если у вас есть другие вопросы, перейдите кReact Hooks FQAПосмотрите, есть большая вероятность, что вопросы, которые вы хотите знать, освещены.

9. Ссылки

Оригинальный адрес:GitHub.com/so coder/no...(Сюда удобнее заходить для общения~)