Интенсивное чтение «Компоненты функций и классов»

JavaScript

1. Введение

Почему вы хотите понять компоненты написания функций? Потому что это становится все более и более важным.

Затем в РеактеЧем функциональный компонент отличается от компонента класса?

how-are-function-components-different-from-classesВ этой статье представлена ​​уникальная точка зрения.

Кстати, вместо Stateless Component в будущем будет использоваться Function Component, поскольку с появлением Hooks функции функциональных компонентов постоянно обогащались, и функциональным компонентам больше не нужно подчеркивать свои характеристики без состояния, поэтому более уместно называть их функциональным компонентом.

2. Обзор

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

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

Capture props

Сравните следующие два фрагмента кода.

Class Component:

class ProfilePage extends React.Component {
  showMessage = () => {
    alert("Followed " + this.props.user);
  };

  handleClick = () => {
    setTimeout(this.showMessage, 3000);
  };

  render() {
    return <button onClick={this.handleClick}>Follow</button>;
  }
}

Function Component:

function ProfilePage(props) {
  const showMessage = () => {
    alert("Followed " + props.user);
  };

  const handleClick = () => {
    setTimeout(showMessage, 3000);
  };

  return <button onClick={handleClick}>Follow</button>;
}

(Онлайн-демонстрация)

Оба компонента описывают одинаковую логику: 3 секунды после нажатия кнопкиalertИмя пользователя, переданное родителем.

Метод вызова родительского компонента выглядит следующим образом:

<ProfilePageFunction user={this.state.user} />
<ProfilePageClass user={this.state.user} />

Затем в течение 3 секунд после нажатия кнопки родитель изменяетсяthis.state.user, Имя пользователя всплывает до или после модификации?

Компонент класса показывает измененное значение:

Функциональный компонент показывает значение до модификации:

Тогда то, что описано в документации ReactpropsРазве это не неизменяемые данные? Почему он все еще меняется во время выполнения?

Причина в том, что хотяpropsнеизменный, даthisявляется изменчивым в компоненте класса, поэтомуthis.propsвызов приведет к доступу к последнемуprops.

И функционального компонента не существуетthis.propsсинтаксис, поэтомуpropsвсегда неизменно.

Для простоты понимания автор добавляет некоторые комментарии к коду:

Function Component:

function ProfilePage(props) {
  setTimeout(() => {
    // 就算父组件 reRender,这里拿到的 props 也是初始的
    console.log(props);
  }, 3000);
}

Class Component:

class ProfilePage extends React.Component {
  render() {
    setTimeout(() => {
      // 如果父组件 reRender,this.props 拿到的永远是最新的。
      // 并不是 props 变了,而是 this.props 指向了新的 props,旧的 props 找不到了
      console.log(this.props);
    }, 3000);
  }
}

Если вы хотите зафиксировать временные реквизиты в компоненте класса, вы можете:const props = this.props;, но такой код хромает, поэтому если вы хотите получить стабильнуюprops, лучше использовать функциональный компонент.

Крючки также имеют функцию захвата значения

См. код ниже:

function MessageThread() {
  const [message, setMessage] = useState("");

  const showMessage = () => {
    alert("You said: " + message);
  };

  const handleSendClick = () => {
    setTimeout(showMessage, 3000);
  };

  const handleMessageChange = e => {
    setMessage(e.target.value);
  };

  return (
    <>
      <input value={message} onChange={handleMessageChange} />
      <button onClick={handleSendClick}>Send</button>
    </>
  );
}

(Онлайн-демонстрация)

по щелчкуSendПосле нажатия кнопки снова измените значение поля ввода, и вывод через 3 секунды по-прежнемуВведите значение поля перед щелчком. Это показывает, что хуки также имеют функцию захвата ценности.

использоватьuseRefФункцию значения захвата можно обойти:

function MessageThread() {
  const latestMessage = useRef("");

  const showMessage = () => {
    alert("You said: " + latestMessage.current);
  };

  const handleSendClick = () => {
    setTimeout(showMessage, 3000);
  };

  const handleMessageChange = e => {
    latestMessage.current = e.target.value;
  };
}

Пока объект присвоения и поиска становитсяuseRef, вместоuseState, вы можете обойти функцию захвата значения и получить последнее значение через 3 секунды.

Это показывает, что использование Function Component + Hooks может обеспечить свойства захвата и значение захвата, чего не может сделать компонент класса, и React официально рекомендуетНовый код, написанный с использованием хуков.

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

оригинальныйhow-are-function-components-different-from-classesРазница между функциональным компонентом и классовым компонентом описана с одной стороны.Причина, по которой функциональный компонент сравнивают с классовым компонентом, почти связана с появлением Hooks API.С помощью хуков возможности функционального компонента можно сравнить с классовым компонентом.

Что касается React Hooks, были представлены два предыдущих интенсивных чтения соответственно:

Однако, несмотря на то, что Hook выпустил стабильную версию, потребуется время, чтобы окружающая среда отреагировала (например,useRouter), сортировка передового опыта потребует времени, поэтому рефакторинг старого кода не рекомендуется.

Чтобы лучше использовать Функциональный компонент, рекомендуется время от времени сравнивать функции Классового компонента, чтобы облегчить понимание и запоминание.

Вот некоторые распространенные проблемы функциональных компонентов:

Настоятельно рекомендуется прочитать полностьюReact Hooks FAQ.

Чем заменить shouldComponentUpdate

Честно говоря, Function Component заменяетshouldComponentUpdateРешение не такое элегантное, как Class Component, код такой:

const Button = React.memo(props => {
  // your component
});

Или напрямую сгенерируйте автономный в родительскомmemoдочерние элементы:

function Parent({ a, b }) {
  // Only re-rendered if `a` changes:
  const child1 = useMemo(() => <Child1 a={a} />, [a]);
  // Only re-rendered if `b` changes:
  const child2 = useMemo(() => <Child2 b={b} />, [b]);
  return (
    <>
      {child1}
      {child2}
    </>
  );
}

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

class Button extends React.PureComponent {}

Так что принеси этоshallowEqualизshouldComponentUpdate.

Как заменить компонентDidUpdate

из-заuseEffectБудет выполняться каждый рендер, поэтому вам нужно смоделироватьuseUpdateфункция:

const mounting = useRef(true);
useEffect(() => {
  if (mounting.current) {
    mounting.current = false;
  } else {
    fn();
  }
});

Больше можно посмотретьИнтенсивное чтение "Как использовать React Hooks для сборки колеса"

Чем заменить forceUpdate

Официальная документация React предлагает решение:

const [ignored, forceUpdate] = useReducer(x => x + 1, 0);

function handleClick() {
  forceUpdate();
}

каждое исполнениеdispatchкогда, покаstateИзменения запускают обновления компонентов. КонечноuseStateТакже можно смоделировать:

const useUpdate = () => useState(0)[1];

мы знаемuseStateЭлемент с индексом 1 используется для обновления данных, и даже если данные не изменятся, компонент будет обновлен при его вызове, поэтому мы можем вернуть неизмененное значение.setValue, так что его функцией остается только компонент обновления.

Больше можно посмотретьИнтенсивное чтение "Как использовать React Hooks для сборки колеса"

Слишком много разделений состояний

useStateТекущая практика заключается в том, чтобы сгладить имя переменной вместо того, чтобы писать его в объекте состояния, таком как компонент класса:

class ClassComponent extends React.PureComponent {
  state = {
    left: 0,
    top: 0,
    width: 100,
    height: 100
  };
}

// VS

function FunctionComponent {
  const [left,setLeft] = useState(0)
  const [top,setTop] = useState(0)
  const [width,setWidth] = useState(100)
  const [height,setHeight] = useState(100)
}

На самом деле состояние также можно агрегировать и управлять им в функциональном компоненте:

function FunctionComponent() {
  const [state, setState] = useState({
    left: 0,
    top: 0,
    width: 100,
    height: 100
  });
}

Просто при обновлении он уже не будет автоматически сливаться, а нужно использовать...stateграмматика:

setState(state => ({ ...state, left: e.pageX, top: e.pageY }));

Как видите, меньше черной магии, больше предсказуемость результатов.

Получить предыдущие реквизиты

Хотя не очень часто используется, в конце концов, компонент класса может пройтиcomponentWillReceivePropsполучатьpreviousPropsа такжеnextProps, для функционального компонента лучше всего получить предыдущее состояние с помощью пользовательских хуков:

function Counter() {
  const [count, setCount] = useState(0);
  const prevCount = usePrevious(count);
  return (
    <h1>
      Now: {count}, before: {prevCount}
    </h1>
  );
}

function usePrevious(value) {
  const ref = useRef();
  useEffect(() => {
    ref.current = value;
  });
  return ref.current;
}

пройти черезuseEffectФункции, которые выполняются после рендеринга компонента, используются повторноuseRefПеременные свойства , пустьusePreviousВозвращаемое значение — «последнее» время рендеринга.

Видимое, разумное использованиеuseEffect useRef, может делать многое, и его по-прежнему удобно использовать, когда он инкапсулирован как CustomHook.

будущееusePreviousВозможно, один из официальных хуков.

Вопросы производительности

useStateХотя параметр функции является начальным значением, так как вся функция Render, она будет вызываться каждый раз при ее инициализации.Если расчет начального значения занимает очень много времени, рекомендуется использовать функцию для передачи, так что он будет выполнен только один раз:

function FunctionComponent(props) {
  const [rows, setRows] = useState(() => createRows(props.count));
}

useRefЭта функция не поддерживается и требуетНапишите несколько избыточных функций, чтобы определить, была ли выполнена инициализация..

Освоив их, Function Component почти ничем не отличается от Class Component!

4. Резюме

Функционал Function Component сравним с Class Component, но текущие передовые практики относительно разбросаны. Некоторые решения, рекомендованные официальными документами, даже не лучше, чем решения сторонних библиотек в сообществе. Ожидается, что функции Компонент класса будет реализован различными способами, те, которые не включены в официальные хуки, на первый взгляд могут показаться ослепительными.

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

Адрес обсуждения:Интенсивное чтение «Компоненты классов VS без сохранения состояния» · Выпуск № 137 · dt-fe/weekly

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

Сфокусируйся наАккаунт WeChat для интенсивного чтения в интерфейсе

special Sponsors

Заявление об авторских правах: Бесплатная перепечатка - некоммерческая - не производная - сохранить авторство (Лицензия Creative Commons 3.0)