React Hooks и Эквивалент жизненного цикла компонентов - PullRequest
0 голосов
/ 12 ноября 2018

Каковы эквиваленты хуков жизненного цикла componentDidMount, componentDidUpdate и componentWillUnmount с использованием хитов React, таких как useEffect?

Ответы [ 2 ]

0 голосов
/ 03 апреля 2019

С Реагировать на документы :

Если вы знакомы с методами жизненного цикла класса React, вы можете подумать использования Effect Hook как componentDidMount, componentDidUpdate и компонентWillUnmount в сочетании.

Под этим высказыванием они подразумевают:

componentDidMount является своего рода useEffect(callback, [])

componentDidUpdate является своего рода useEffect(callback, [dep1, dep2, ...]) - массив deps сообщает React: "если один из deps изменяется, запустите обратный вызов после рендеринга" .

componentDidMount + componentDidUpdate является своего рода useEffect(callback)

componentWillUnmount является сортировкой возвращенной функции из обратного вызова:

useEffect(() => { 
    /* some code */
    return () => { 
      /* some code to run when rerender or unmount */
    }
)

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

Хотя вы можете использовать эти крючки, это не является точным эквивалентом. В отличие от componentDidMount и componentDidUpdate, он будет захватывать реквизита и состояния. Так что даже внутри обратных вызовов вы увидите реквизиты и состояние конкретного рендера (что означает componentDidMount исходные реквизиты и состояние). Если вы хотите увидеть что-то «последнее», вы можете написать это в ссылку. Но обычно есть более простой способ структурировать код, чтобы вам не пришлось это делать. Возвращаемая функция, предполагающая альтернативу componentWillUnmount, также не является точным эквивалентом, поскольку функция будет запускаться каждый раз, когда компонент будет перерисовываться, и когда компонент будет размонтирован. Имейте в виду, что ментальная модель для эффектов отличается от жизненных циклов компонентов, и попытка найти их точные эквиваленты может сбить вас с толку больше, чем помочь. Чтобы стать продуктивным, вам нужно «мыслить эффектами», и их ментальная модель ближе к реализации синхронизации, чем к реагированию на события жизненного цикла.

Пример из блога Дэна:

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

  useEffect(() => {
    setTimeout(() => {
      console.log(`You clicked ${count} times`);
    }, 3000);
  });

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

enter image description here

Если мы используем реализацию класса:

componentDidUpdate() {
  setTimeout(() => {
    console.log(`You clicked ${this.state.count} times`);
  }, 3000);
}

enter image description here

this.state.count всегда указывает на последний счет, а не тот, который принадлежит определенному рендеру.

0 голосов
/ 12 ноября 2018

componentDidMount

Передать пустой массив в качестве второго аргумента useEffect() для запуска только обратного вызова только при монтировании.

function ComponentDidMount() {
  const [count, setCount] = React.useState(0);
  React.useEffect(() => {
    console.log('componentDidMount');
  }, []);

  return (
    <div>
      <p>componentDidMount: {count} times</p>
      <button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        Click Me
      </button>
    </div>
  );
}

ReactDOM.render(
  <div>
    <ComponentDidMount />
  </div>,
  document.querySelector("#app")
);
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>

<div id="app"></div>

componentDidUpdate

componentDidUpdate() вызывается сразу после обновления.Этот метод не вызывается для начального рендеринга.useEffect выполняется на каждом рендере, включая первый.Поэтому, если вы хотите иметь строгий эквивалент componentDidUpdate, вы должны использовать useRef, чтобы определить, был ли компонент смонтирован один раз.Если вы хотите быть еще строже, используйте useLayoutEffect(), но он срабатывает синхронно.В большинстве случаев useEffect() должно быть достаточно.

Этот ответ вдохновлен Толле , вся заслуга ему.

function ComponentDidUpdate() {
  const [count, setCount] = React.useState(0);

  const isFirstUpdate = React.useRef(true);
  React.useEffect(() => {
    if (isFirstUpdate.current) {
      isFirstUpdate.current = false;
      return;
    }

    console.log('componentDidUpdate');
  });

  return (
    <div>
      <p>componentDidUpdate: {count} times</p>
      <button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        Click Me
      </button>
    </div>
  );
}

ReactDOM.render(
  <ComponentDidUpdate />,
  document.getElementById("app")
);
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>

<div id="app"></div>

componentWillUnmount

Возвращает обратный вызов в аргументе обратного вызова useEffect, и он будет вызван перед размонтированием.

function ComponentWillUnmount() {
  function ComponentWillUnmountInner(props) {
    React.useEffect(() => {
      return () => {
        console.log('componentWillUnmount');
      };
    }, []);

    return (
      <div>
        <p>componentWillUnmount</p>
      </div>
    );
  }
  
  const [count, setCount] = React.useState(0);

  return (
    <div>
      {count % 2 === 0 ? (
        <ComponentWillUnmountInner count={count} />
      ) : (
        <p>No component</p>
      )}
      <button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        Click Me
      </button>
    </div>
  );
}

ReactDOM.render(
  <div>
    <ComponentWillUnmount />
  </div>,
  document.querySelector("#app")
);
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>

<div id="app"></div>
...