Когда реагируют, выполните componentDidMount и componentWillUnmount

Я играл с React несколько лет, но в некоторых случаях до сих пор путаюсь с механизмом монтирования/размонтирования.

Поскольку mount/unmount — это место для выполнения побочного эффекта, я не хочу, чтобы они вызывались случайным образом. Поэтому мне нужно выяснить, как они работают. Насколько я понимаю в настоящее время, когда виртуальный дом не присутствует в реальном доме, он, как правило, размонтируется. Однако, похоже, это не вся история, и я не могу об этом рассуждать.

function TestMount(props) {
  useEffect(() => {
    console.info("componentDidMount", props.name);
    return () => {
      console.info("componentWillUnount", props.name);
    };
  }, []);
  return <h1>Test content {" " + JSON.stringify(props.name)}</h1>;
}

function Update({ click }) {
  return <button onClick = {click}>Update</button>;
}

function App() {
  const [count, setCount] = useState(0);
  const Component = name => <TestMount name = {name} />;
  return (
    <div className = "App">
      <h1>{count}</h1>
      <Component name = "one" />
      {Component("two")}
      <Update click = {() => setCount(x => x + 1)} />
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Первый компонент перемонтирует рендеринг приложения, а второй нет? Почему это происходит?

Потому что {name: 'one'} !== {name: 'one'}, но 'two' === 'two'

zerkms 09.05.2019 06:26

@zerkms не видит, как это связано с механизмом монтирования в реакции

Guichi 09.05.2019 06:40

@zerkms Что действительно актуально, так это (()=>'one')!==(()=>'one'),но 'two'==='two'

Guichi 13.11.2019 05:21

Я не уверен, почему вы так думаете, у вас есть {name: 'one'} реквизит, и я не вижу, где у вас есть анонимная функция () => 'one'

zerkms 13.11.2019 07:14

@zerkms ()=>'one' на самом деле является легальным компонентом реакции, другой ()=>'one' является компонентом реакции еще один, если он имеет другую идентичность. Если для создания элемента инициализируются разные компоненты, реакция отключит первый компонент и перемонтирует второй компонент вместо обновления «того же»

Guichi 13.11.2019 07:20

Не уверен, насколько это относится к вашему вопросу. У вас есть {name: 'one'} реквизиты в опубликованном вами коде (в этом выражении: <Component name = "one" />). Я не уверен, что вижу ()=>'one' где-нибудь в вашем коде. Важно то, что вы на самом деле пройти{name: 'one'}.

zerkms 13.11.2019 07:27
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
1
6
503
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Component — это новая функция каждый раз, когда App визуализируется, поэтому <Component name = "one" /> тоже каждый раз перемонтируется, они считаются разными компонентами.

Результатом вызова Component("two") является <TestMount name = {"two"} />, TestMount остается неизменным при каждом рендеринге App, поэтому он не перемонтируется.

Component является недопустимым компонентом для того, для чего он используется, для передачи name строки в качестве name реквизита для TestMount компонента, потому что параметр name не является строкой, а объект реквизита, когда Component используется как <Component name = "one" />. name => <TestMount name = {name} /> — это функция рендеринга, для ясности предпочтительнее назвать его соответственно, например, renderTestMount, потому что компоненты не должны вызываться напрямую, как Component("two").

В случае, если предполагается, что функция используется как компонент или функция рендеринга взаимозаменяемо, сигнатура должна быть изменена на ({ name }) => <TestMount name = {name} />.

Ожидаемое поведение может быть достигнуто для <Component name = "one" /> путем запоминания Component:

const Component = useCallback(({ name }) => <TestMount name = {name} />, []);

Но так как Component не зависит от области App, правильный способ — определить его снаружи:

const Component = ({ name }) => <TestMount name = {name} />;

function App() {...}

Например, по этой причине React Router Route имеет отдельные реквизиты component и render для компонента и функция рендеринга. Это позволяет предотвратить ненужные перемонтирования для компонентов маршрута, которые необходимо определять динамически в текущей области.

Спасибо за ваш ответ, разница между render и component в react-router внезапно стала для меня понятна. Суть проблемы в том, что React хорош в element, а не в Component, просто обратите внимание, что вы не можете useCallback снаружи App из-за ограничений хука, lodash.once должно быть то, что вы имеете в виду

Guichi 09.05.2019 08:45

Пожалуйста. Вам не нужен useCallback вне приложения, компонент уже остается неизменным между рендерами, таким образом, это обычный компонент.

Estus Flask 09.05.2019 08:51

«Компонент является недопустимым компонентом, потому что он принимает имя в качестве аргумента вместо реквизита» --- как так? Это функция, и у нее могут быть аргументы с любым именем, будь то name или foo.

zerkms 09.05.2019 10:25

@zerkms Параметр может иметь любое имя, но то, что это name, предполагает, что он должен получить строку имени в качестве аргумента, например Component("two"), в то время как он получает объект реквизита, когда он используется в качестве компонента.

Estus Flask 09.05.2019 10:36

@estus это не делает это неверный: 1) Имя аргумента ничего не меняет 2) Можно передать объект как свойство компонента реакции. Я признаю, однако, что неверно истолковываю термин неверный.

zerkms 09.05.2019 11:39

@zerkms <Component name = "one" /> и {Component("two")} не взаимозаменяемы. Для намерения OP недопустимо, чтобы TestMount получал строку как name опору. Я обновлю ответ, чтобы прояснить ситуацию.

Estus Flask 09.05.2019 11:45

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

zerkms 09.05.2019 11:46
Ответ принят как подходящий

Ключом к этой проблеме является разница между компонентом React и элементом React, короче говоря, React умный с element не Component

Компонент против элемента

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

Как React выполняет обновление между рендерами

Каждый раз, когда вызывается метод render (или функциональный компонент). Новый элемент создается с помощью <>, однако React достаточно умен, чтобы сказать, что элемент, созданный между рендерами, на самом деле один и тот же, то есть он был создан ранее и может быть повторно использован пока элемент создается одним и тем же компонентом

Как насчет другого компонента

Однако, когда идентификация компонента, используемого для создания элемента, изменяется (даже если компоненты выглядят одинаково), React считает, что появилось что-то новое, поэтому он удаляет (размонтирует) предыдущий элемент и добавляет (монтирует) новый. Таким образом, вызывается componentDidMount или componentWillUnmount.

Как сбивает с толку

Думаю, у нас есть Component, и когда мы генерируем element с помощью <Component />, реакция может указать одни и те же элементы, потому что они генерируются одним и тем же Component Однако HOCComponent=()=><Component />; element= <HOCComponent /> каждый раз, когда генерируется element, используется другой Component. на самом деле это HOC, построенный динамически. Поскольку HOC создается динамически внутри функции рендеринга, на первый взгляд это может сбить с толку.

Это правда

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

function TestMount(props) {
  useEffect(() => {
    console.info("componentDidMount", props.name);
    return () => {
      console.info("componentWillUnount", props.name);
    };
  }, []);
  return <h1>Test content {" " + JSON.stringify(props.name)}</h1>;
}

function Update({ click }) {
  return <button onClick = {click}>Update</button>;
}

let _Component;
function cacheComponent(C) {
  if (C && !_Component) {
    _Component = C;
  }
  return _Component || null;
}

const CacheComponent2 = once(({ name }) => <TestMount name = {name} />, []);

function App() {
  const [count, setCount] = useState(0);
  // can be used as a HOC of TestMount or a plain function returnnung a react element
  const Component = name => <TestMount name = {name} />;
  const CacheComponent1 = cacheComponent(Component);
  const CacheComponent3 = useCallback(
    ({ name }) => <TestMount name = {name} />,
    []
  );

  return (
    <div className = "App">
      <h1>{count}</h1>
      {/* used as HOC */}
      <Component name = "one" />
      {/* used as function returnning the element */}
      {Component("two")}
      <CacheComponent1 name = "three" />
      <CacheComponent2 name = "four" />
      <CacheComponent3 name = "five" />
      <Update click = {() => setCount(x => x + 1)} />
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Edit react-unmount-case

Также приведенный выше код предоставляет три различных способа избежать нежелательного монтирования/размонтирования. Все решения каким-то образом кэшируют личность HOC

Другие вопросы по теме