React Hooks и Эквивалент жизненного цикла компонентов

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

Ответы

Ответ 1

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/[email protected]/umd/react.development.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>

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

Ответ 2

Из React документов:

Если вы знакомы с методами жизненного цикла класса React, вы можете использовать useEffect Hook как componentDidMount, componentDidUpdate и componentWillUnmount вместе.

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

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 всегда указывает на последний счет, а не на тот, который принадлежит определенному рендеру.