Владислав Калачев

Учим useEffect на примерах — React Hooks

3 628

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

Для лучшего понимания работы useEffect, мы рассмотрим пример решения задачи с использованием React класса, а затем перепишем этот функционал, непосредственно применяя useEffect.

Видео на эту тему.

Побочные эффекты в классах

Посмотрим на следующий пример кода:


export default class ExampleClass extends React.Component {
  state = {
    x: 0,
    y: 0,
    moveCount: 0,
    cross: 0
  };

  mouseMoveHandler = event => {
    this.setState({
      x: event.clientX,
      y: event.clientY
    });
  };

  componentDidMount() {
    console.log("componentDidMount");
    window.addEventListener("mousemove", this.mouseMoveHandler);
  }

  componentDidUpdate(prevProps, prevState) {
    const { x, y } = this.state;
    if (x !== prevState.x || y !== prevState.y) {
      this.setState(prev => {
        return {
          moveCount: prev.moveCount + 1
        };
      });
    }
  }

  static getDerivedStateFromProps(props, state) {
    if (state.x === state.y) {
      return {
        cross: state.x
      };
    }
    return null;
  }

  componentWillUnmount() {
    console.log("componentDidUnmount");
    window.removeEventListener("mousemove", this.mouseMoveHandler);
  }

  render() {
    const { x, y, moveCount, cross } = this.state;

    return (
      <div>
        <p>
          Координаты мыши: {x}, {y}
        </p>
        <p>Счетчик движения мыши: {moveCount} сек</p>
        <p>
          Позиция пересечения двух точек: {cross}, {cross}
        </p>
      </div>
    );
  }
}

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

В componentDidMount мы добавляем событие mousemove в виде функции  mouseMoveHandler, в которой мы будем присваивать текущие координаты мыши. Условие проверки изменения координат в componentDidUpdate позволяет менять состояние счётчика при движении мыши. Таким образом, когда мы двигаем курсор, счетчик будет «наматывать» свое значение. Для расчета позиции двух точек был использован getDerivedStateFromProps (можно перенести данный код в componentDidUpdate), в котором мы просто сравниваем координаты x и y. Если они равны, то мы обновляем состояние и добавляем новое значение cross. 

В componentWillUnmount происходит удаление подписки на событие mousemove из DOM.

На текущем примере мы разобрали работу жизненного цикла React-компонента в рамках решения задачи с координатами мыши.

Введение в useEffect

Хук useEffect решает задачи всех используемых в примере методов жизненного цикла (componentDidMount, componentDidUpdate, и componentWillUnmount). Прежде чем мы приступим к замене React класса на функциональный компонент, посмотрим какие параметры принимает useEffect и с какими условиями его можно вызывать.

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

Пример:


useEffect(() => { 
  console.log('render')
});

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

Вторым параметром useEffect является массив с зависимостями, главная цель которого следить за обновлениями в состоянии и при их изменении перерендеривать компонент (соотвественно снова вызывать useEffect). Если оставить массив пустым, то useEffect будет вызываться один раз после успешного добавления компонента (данная логика повторяет работу componentDidMount).

Пример:


useEffect(() => {
	console.log('mounted')
}, []);

Для того, чтобы воспроизвести в useEffect работу componentWillUnmount, требуется вернуть функцию из функции, где мы описываем логику событий, происходящих после удаления компонента. 

Пример:


useEffect(() => {
  return () => {
    console.log('will unmount');
  }
}, []);

Теперь, после изучения теоретической составляющей о useEffect и его работе, мы готовы  переписать первоначальный пример, используя React хуки.

Использование useEffect

Чтобы начать использовать хуки, требуется переписать React класс в React компонент функцию.  Для создания состояния воспользуемся хуком useState

Пример:


export default props => {
  const [x, setX] = useState(0);
  const [y, setY] = useState(0);
  const [moveCount, setMoveCount] = useState(0);
  const [cross, setCross] = useState(0);

return (
  <div>
      <p>
        Координаты мыши: {x}, {y}
      </p>
      <p>
	Счетчик движения мыши: {moveCount} сек
	</p>
      <p>
        Позиция пересечения двух точек: {cross}, {cross}
      </p>
    </div>
)}

Вызовем обработчик события мыши, когда компонент будет добавлен в DOM и удалим его, когда компонент будет удален. При перемещении мыши будем устанавливать новые координаты.

Пример:


export default props => {
  const [x, setX] = useState(0);
  const [y, setY] = useState(0);
  const [moveCount, setMoveCount] = useState(0);
  const [cross, setCross] = useState(0);

  const mouseMoveHandler = event => {
    setX(event.clientX);
    setY(event.clientY);
  };

  useEffect(() => {
    console.log("componentDidMount");
    document.addEventListener("mousemove", mouseMoveHandler);
    return () => {
      console.log("componentDidUnmount");
      document.removeEventListener("mousemove", mouseMoveHandler);
    };
  }, []);

  return (
    <div>
      <p>
        Координаты мыши: {x}, {y}
      </p>
      <p>
	Счетчик движения мыши: {moveCount} сек
      </p>
      <p>
        Позиция пересечения двух точек: {cross}, {cross}
      </p>
    </div>
  );
};

Осталось добавить логику со счётчиком движения мыши и позиции пересечения двух точек. Для удобства напишем useEffect еще два раза. В первом вызове укажем условие, где координата x === y, добавим x в cross, как точку пересечения двух осей.

Пример:


useEffect(() => {
    console.log("componentDidUpdate");
    if (x === y) {
      setCross(x);
    }
});

Во втором useEffect добавим конечным параметром массив с зависимостями x и y. Внутри функции первого параметра вызовем setMoveCount, который будет добавлять +1 каждый раз при изменении координат (x,y) указателя мыши.

Пример:

 
useEffect(() => {
   setMoveCount(moveCount => moveCount + 1);
}, [x, y]);

Посмотрим что у нас получилось в финальной версии кода:


export default props => {
  const [x, setX] = useState(0);
  const [y, setY] = useState(0);
  const [moveCount, setMoveCount] = useState(0);
  const [cross, setCross] = useState(0);

  const mouseMoveHandler = event => {
    setX(event.clientX);
    setY(event.clientY);
  };

  useEffect(() => {
    console.log("componentDidUpdate");
    if (x === y) {
      setCross(x);
    }
  });

  useEffect(() => {
    console.log("componentDidMount");
    document.addEventListener("mousemove", mouseMoveHandler);
    return () => {
      console.log("componentDidUnmount");
      document.removeEventListener("mousemove", mouseMoveHandler);
    };
  }, []);

  useEffect(() => {
    setMoveCount(moveCount => moveCount + 1);
  }, [x, y]);

  return (
    <div>
      <p>
        Координаты мыши: {x}, {y}
      </p>
      <p>Счетчик движения мыши: {moveCount} сек</p>
      <p>
        Позиция пересечения двух точек: {cross}, {cross}
      </p>
    </div>
  );
};

Сегодня мы разобрали на примере работы с координатами мыши использование хук useEffect. Также посмотрели из каких параметров он состоит и с какими условиями его можно вызывать. Надеюсь, что данная статья была вам полезна. Исходный код вы сможете найти тут. Учитесь, думайте, пишите код. Удачного кодинга, друзья!

Полезные материалы:

https://ru.reactjs.org/docs/hooks-effect.html — официальная документация по React Hooks c  примерами использования useEffect.

Подписывайтесь на наш канал в Telegram и на YouTube для получения самой последней и актуальной информации. 

Комментарии:

Учим useEffect на примерах — React Hooks: 2 комментария

  1. в классах таким образом, чтобы это значение указывало бы на самую свежую версию состояния), а не механизм замыканий. Замыкания — это отличный инструмент в том случае, если значение, которое «запирают» в замыкании, никогда не меняется. Это облегчает их использование и размышления о них, так как, в сущности, речь идёт о константах. И, как мы уже говорили, свойства и состояние никогда не меняются в конкретном рендере. Да, кстати, версию этого примера, в которой используются компоненты, основанные на классах, можно исправить, воспользовавшись замыканием .

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *