Интенсивное чтение «React Hooks»

внешний интерфейс React.js Redux ESLint

1. Введение

Реагирующие хуки — это React16.7.0-alphaНовые функции, представленные в версии, студенты, которые хотят попробовать, могут установить эту версию.

Проблема, которую решают React Hooks, — это совместное использование состояния., является следующееrender-propsа такжеhigher-order componentsТретья схема совместного использования состояния после этого не вызовет проблемы ада вложенности JSX.

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

Статья, которой я поделился не так давноИсходный код Epitath — новое использование renderPropsЭто решить проблему гнездования JSX. С помощью реактивных крюков эта проблема была официально решена.

Чтобы быстрее понять, что такое React Hooks, давайте сначала посмотрим на следующий код renderProps, процитированный автором:

function App() {
  return (
    <Toggle initial={false}>
      {({ on, toggle }) => (
        <Button type="primary" onClick={toggle}> Open Modal </Button>
        <Modal visible={on} onOk={toggle} onCancel={toggle} />
      )}
    </Toggle>
  )
}

Как оказалось, React Hooks также решает эту проблему:

function App() {
  const [open, setOpen] = useState(false);
  return (
    <>
      <Button type="primary" onClick={() => setOpen(true)}>
        Open Modal
      </Button>
      <Modal
        visible={open}
        onOk={() => setOpen(false)}
        onCancel={() => setOpen(false)}
      />
    </>
  );
}

Как видите, React Hooks похож на встроенную плоскую библиотеку renderProps, мы можем создать значение в любое время и изменить метод этого значения. Это выглядит как setState в виде функции, что фактически эквивалентно внедрению зависимостей по сравнению с использованием setState,Этот компонент не имеет состояния.

2 Обзор

Преимущества, принесенные реактивными крючками, являются не только «больше FP, более тонким обновлением гранулярности и более четкому коду», но и следующие три функция:

  1. Множественные состояния не будут вложены друг в друга, а метод записи по-прежнему мозаичен (renderProps можно решить композицией, но это не только немного громоздко в использовании, но и увеличивает количество сущностей из-за принудительной инкапсуляции нового объекта).
  2. Хуки могут ссылаться на другие хуки.
  3. Проще отделить пользовательский интерфейс компонента от состояния.

Давайте расширим второй пункт: хуки могут ссылаться на другие хуки, мы можем сделать это:

import { useState, useEffect } from "react";

// 底层 Hooks, 返回布尔值:是否在线
function useFriendStatusBoolean(friendID) {
  const [isOnline, setIsOnline] = useState(null);

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

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

  return isOnline;
}

// 上层 Hooks,根据在线状态返回字符串:Loading... or Online or Offline
function useFriendStatusString(props) {
  const isOnline = useFriendStatusBoolean(props.friend.id);

  if (isOnline === null) {
    return "Loading...";
  }
  return isOnline ? "Online" : "Offline";
}

// 使用了底层 Hooks 的 UI
function FriendListItem(props) {
  const isOnline = useFriendStatusBoolean(props.friend.id);

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

// 使用了上层 Hooks 的 UI
function FriendListStatus(props) {
  const statu = useFriendStatusString(props.friend.id);

  return <li>{statu}</li>;
}

В этом примере есть два хука:useFriendStatusBooleanа такжеuseFriendStatusString, useFriendStatusStringэто использоватьuseFriendStatusBooleanСгенерированный новый хук, эти два хука могут дать разный пользовательский интерфейс:FriendListItem,FriendListStatusИспользуйте, а поскольку данные двух хуков связаны, состояние двух пользовательских интерфейсов также связано.

Кстати, этот пример тоже можно использовать для пониманияНекоторые мысли о React HooksФраза статьи:«Нет рендеринга, нет положения рендеринга»:

  • useFriendStatusBooleanа такжеuseFriendStatusStringявляется компонентом с состоянием (используяuseState), без рендеринга (возвращает значение, отличное от пользовательского интерфейса), поэтому его можно использовать какCustom HooksВызывается любым компонентом пользовательского интерфейса.
  • FriendListItemа такжеFriendListStatusпредставляет собой отображаемый компонент (возвращается JSX), без состояния (не используетсяuseState), который является чисто функциональным компонентом пользовательского интерфейса,

Создайте Redux с помощью useState

Суть Redux — это Редюсер, и React Hooks может легко создать механизм Redux:

// 这就是 Redux
function useReducer(reducer, initialState) {
  const [state, setState] = useState(initialState);

  function dispatch(action) {
    const nextState = reducer(state, action);
    setState(nextState);
  }

  return [state, dispatch];
}

Часть значения этого пользовательского хука используется как состояние избыточности, а часть setValue используется как отправка избыточности, которая вместе является избыточностью. И соединяющая часть react-redux делает то же самое, что и вызов Hook:

// 一个 Action
function useTodos() {
  const [todos, dispatch] = useReducer(todosReducer, []);

  function handleAddClick(text) {
    dispatch({ type: "add", text });
  }

  return [todos, { handleAddClick }];
}

// 绑定 Todos 的 UI
function TodosUI() {
  const [todos, actions] = useTodos();
  return (
    <>
      {todos.map((todo, index) => (
        <div>{todo.text}</div>
      ))}
      <button onClick={actions.handleAddClick}>Add Todo</button>
    </>
  );
}

useReducerБыл использован как встроенный хуки, здесь вы можете проверить всеВстроенные крючки.

Однако здесь следует отметить, что каждыйuseReducerИли ваши собственные пользовательские хуки не сохранят данные, поэтому, например, мы создаем два приложения, App1 и App2:

function App1() {
  const [todos, actions] = useTodos();

  return <span>todo count: {todos.length}</span>;
}

function App2() {
  const [todos, actions] = useTodos();

  return <span>todo count: {todos.length}</span>;
}

function All() {
  return (
    <>
      <App1 />
      <App2 />
    </>
  );
}

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

Если вы хотите по-настоящему реализовать функцию Redux, то есть поддерживать глобальное состояние, любой компонентuseReducerоба имеют доступ к одним и тем же данным и могутuseContextиспользовать вместе.

Общая идея заключается в использованииuseContextОбщие данные, как пользовательские крючки источника данных. Вы можете обратиться к конкретной реализацииredux-react-hook.

Замените некоторые жизненные циклы на useEffect

Рядом с расположением useState вы можете использовать useEffect для обработки побочных эффектов:

useEffect(() => {
  const subscription = props.source.subscribe();
  return () => {
    // Clean up the subscription
    subscription.unsubscribe();
  };
});

useEffectКод будет выполняться как при инициализации, так и при каждом последующем ререндеринге, а возвращаемое значение выполняется при уничтожении. Это удобнее, сравните процесс вызова G2 версии React:

class Component extends React.PureComponent<Props, State> {
  private chart: G2.Chart = null;
  private rootDomRef: React.ReactInstance = null;

  componentDidMount() {
    this.rootDom = ReactDOM.findDOMNode(this.rootDomRef) as HTMLDivElement;

    this.chart = new G2.Chart({
      container: document.getElementById("chart"),
      forceFit: true,
      height: 300
    });
    this.freshChart(this.props);
  }

  componentWillReceiveProps(nextProps: Props) {
    this.freshChart(nextProps);
  }

  componentWillUnmount() {
    this.chart.destroy();
  }

  freshChart(props: Props) {
    // do something
    this.chart.render();
  }

  render() {
    return <div ref={ref => (this.rootDomRef = ref)} />;
  }
}

С помощью React Hooks вы можете сделать это:

function App() {
  const ref = React.useRef(null);
  let chart: G2.Chart = null;

  React.useEffect(() => {
    if (!chart) {
      chart = new G2.Chart({
        container: ReactDOM.findDOMNode(ref.current) as HTMLDivElement,
        width: 500,
        height: 500
      });
    }

    // do something
    chart.render();

    return () => chart.destroy();
  });

  return <div ref={ref} />;
}

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

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

3 Интенсивное чтение

Обещание, принесенное Крюками

Функция Hook должна начинаться с имени «use», потому что eslint удобно проверять и предотвращать обертывание оператора useHook оценкой условия.

Почему нельзя обернуть оператор useHook условием?официальная документация, вот краткое введение.

Перехватчики React не реализуются через прокси или геттеры (подробности см. в этой статье).React hooks: not magic, just arrays), но через массив каждый разuseStateизменит индекс, еслиuseStateзавернуто в условие, нижний индекс каждого выполнения может не совпадать, что приводит кuseStateэкспортируетсяsetterОбновить неправильные данные.

Хотя естьeslint-plugin-react-hooksПлагины сопровождаются, но это первый случай, когда концепция «сначала соглашение» была введена в структуру React, что принесло беспрецедентныйОграничения по наименованию кода и заказу(Именование функций официально ограничено, и либертарианцы JS могут быть в ярости), но удобство, которое оно приносит, беспрецедентно (нет лучшего решения для разделения состояния, чем React Hooks, соглашения приносят эффективность, а цена свободы — вернуться к renderProps или HOC каждая команда может оценить самостоятельно).

Автор считает, что рождение React Hooks может быть связано с этим вдохновением: «Лучше полностью решить проблему разделения состояния, добавив некоторые соглашения!»

Соглашения React больше, чем строительные леса конфигурацииnextjs umiИ авторpriВсе они имеют функцию «обычной маршрутизации», что значительно снижает сложность настройки маршрутизации.Тогда React Hooks подобны соглашениям на уровне кода., что значительно снижает сложность кода.

Граница между состоянием и пользовательским интерфейсом станет четче

Из-за особенностей хуков React, если хук не генерирует пользовательский интерфейс, он всегда может быть инкапсулирован другими хуками.useEffectВ целом вполне функционален. Хуки должны быть написаны в верхней части функций пользовательского интерфейса, и легко выработать полезную привычку писать компоненты пользовательского интерфейса без состояния, и будет легче практиковать концепцию «разделения состояния и пользовательского интерфейса».

Но эта идея немного дерьмовое место, то есть «государство» в конце концов, что это такое.

function App() {
  const [count, setCount] = useCount();
  return <span>{count}</span>;
}

мы знаемuseCountОн не имеет состояния, потому что суть React Hooks — это еще один способ написания renderProps или HOC, это легко понять, если заменить его на renderProps:

<Count>{(count, setCount) => <App count={count} setCount={setCount} />}</Count>;

function App(props) {
  return <span>{props.count}</span>;
}

Вы можете видеть, что компонент App не имеет состояния, а вывод полностью определяется вводом (Props).

Тогда компонент с состоянием и без пользовательского интерфейса будетuseCountсейчас:

function useCount() {
  const [count, setCount] = useState(0);
  return [count, setCount];
}

Stateful место должно относиться кuseState(0)Это предложение, но это предложение связано с приложением компонента пользовательского интерфейса без сохранения состояния.useCount()Так же, как React помещаетuseCountстать пользовательским хуком, затемuseStateЭто официальный хук, который имеет такое же определение, поэтому можно считать, чтоuseCountне имеет гражданства,useStateЭто также слой renderProps, и конечное состояние на самом делеuseStateЭто встроенный компонент React.

Давайте посмотрим на вложенное выражение renderProps:

<UseState>
  {(count, setCount) => (
    <UseCount>
      {" "}
      {/**虽然是透传,但给 count 做了去重,不可谓没有作用 */}
      {(count, setCount) => <App count={count} setCount={setCount} />}
    </UseCount>
  )}
</UseState>

Несомненно то, что приложение должно иметь пользовательский интерфейс, а два родительских компонента выше не должны иметь пользовательского интерфейса. Для лучшей практики мы пытаемся избежать сохранения состояния приложением само по себе, в то время как его родительский компонент RenderProps может поддерживать состояние (или нет, и быть установщиком). Таким образом, вы можете добавить предложение после предложения «компоненты с состоянием не визуализируются, а визуализированные компоненты не имеют состояния»: компоненты, которые не визуализируются, также могут быть без состояния.

4 Резюме

Думайте о React Hooks как о более удобном RenderProps. Хотя метод записи, кажется, поддерживает состояние внутри, на самом деле он эквивалентен инъекции, Connect, HOC или renderProps. Таким образом, порог для использования renderProps будет значительно снижен, потому что хуки настолько удобны в использовании, что мы можем абстрагировать большое количество пользовательских хуков, чтобы сделать код более FP без увеличения уровня вложенности.

еще 5 обсуждений

Адрес обсуждения:Интенсивное чтение хуков React · Выпуск №111 · dt-fe/weekly

Если вы хотите участвовать в обсуждении, пожалуйста,кликните сюда, с новыми темами каждую неделю, выходящими по выходным или понедельникам. Интерфейс интенсивного чтения — поможет вам отфильтровать надежный контент.