Русский
Русский
English
Статистика
Реклама

В чем разница между useLayoutEffect, componentDidMount, useEffect

Привет хабр!

В React на смену эпохи классов, пришла эпоха функциональных компонентов. И нам показали хуки, как замена методам жизненного цикла. Но многие так и не задумывались, а равнозначный ли обмен componentDidMount на useEffect. Эта статья направлена как раз на таких людей, чтобы закрыть ваш пробел, в том как работают componentDidMount, useEffect и useLayoutEffect. (данная статья является расшифровкой видео)

Викторина

Для того чтобы разобраться в этом вопросе, проведем небольшую викторину!

Постановка задачи

Допустим у нас есть красный блок с некоторой высотой и шириной. И у нас есть задача вывести ширину этого блока на экран:

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

const App extends Component {  state = { width: 0 };  ref = React.createRef();  componentDidMount() {    this.setState({ width: this.ref.current.clientWidth });  }  render() {    return (      <div className="app">        <div className="block" ref={this.ref} />        <span className="result">          width: <b>{this.state.width}</b>        </span>      </div>    )  }}

Интерес данной ситуации состоит в том, что изначально у нас в state хранится ширина равная нолю (state = { width: 0 };). Потом происходит render. И уже только после рендера в componentDidMount вызывается this.setState({ ... }) с реальным значением ширины блока. И снова происходит render с уже обновленным значением this.state.width

Вопрос

Что увидит пользователь в браузере? Сначала ширина будет ноль, а потом быстро изменится на реальное значение? Или сразу увидим реальное значение ширины блока?

Как всегда даем минутку подумать .

Ответ

И правильный ответ: width сразу отобразит цифру 220, без промежуточного значения 0. Результат достаточно интересный, чтобы лучше разобраться в текущей ситуации проведем еще один тест.

Анализ происходящего

Давайте создадим функцию sleep. Только не асинхронную с помощью setTimeout, а наоборот синхронную, чтобы блокировала поток, для этого зациклим while на присланное количество миллисекунд:

sleep(duration) {  const start = new Date().getTime();  let end = start;  while(end < start + duration) {    end = new Date();getTime();  }}

И теперь добавим sleep с 3000 миллисекунд до присвоения значения width в state

componentDidMount() {  this.sleep(3000);  this.setState({ width: this.ref.current.clientWidth });}

Как думаете что теперь пользователь увидит?

Результат снова немного неожиданный, у нас страница просто фризится на 3 секунды и только после этого браузер отрисует красный квадрат и сразу же выдаст цифру с шириной квадрата. Из этого уже можно сделать какие то выводы

Выводы

Получается на основе render создается виртуальное дерево, но перед тем как отдать виртуальное дерево на отрисовку в браузер, вызывается componentDidMount и даже более того блокирует отрисовку в браузере, в нашем случае на 3 секунды. Три, два, один и setState заново перестраивает виртуальное дерево, и только после всего этого браузер рисует страницу. И даже если указать задержку не 3 секунды, а 30 секунд, результат не изменится мы увидим как страница зависнет на 30 секунд.

useEffect

Давайте теперь сравним с тем как работает useEffect. Напишем такой же код на функциональном компоненте:

const App = () => {  const [width, setWidth] = useState(0);  const ref = useRef();    useEffect(() => {    let start = new Date().getTime();    let end = start;    while (end < start + 3000) {      end = new Date().getTime();    }        setWidth(ref.current.clientWidth);  }, []);    return (    <div className="app">      <div className="block" ref={ref} />      <span className="result">        width: <b>{width}</b>      </span>    </div>  )}

Результат как вы уже догадались будет отличаться. Мы увидим сначала значение ноль, а только через 3 секунды цифра обновится до ширины блока. Таким образом можно предположить что useEffect работает по следующему сценарию:

Сначала на основе return создается виртуальное дерево, далее оно отдается на отрисовку в браузер и только после этого вызывается функция переданная в useEffect, в которой уже идет блокировка потока на 3 секунды, три, два, один и после спячки виртуальное дерево заново строится с новым значением в state и это дерево передается на отрисовку в браузер

Документация

Теперь мы точно знаем, что componentDidMount отличается от useEffect. Чтобы понять, это отличие вызвано просто особенностями реализации хуков или же умышленно, давайте как всегда обратимся к документации.

Из этого можно сделать вывод, что React разработчики умышленно дали нам, абсолютно новый API, позволяющий отрисовать контент в браузере до того как запуститься функция переданная в useEffect. Этот API просто не существует при написании компонента на классах. Его можно сымитировать разве что использовав setTimeout внутри componentDidMount.

componentDidMount() {  setTimeout(() => {    ...  }, 0);}

Но это скорее выглядит как костыль чем решение.

useLayoutEffect

А с другой стороны, если нам нужно выполнить какой то код, до отрисовки в браузере, на предоставили хук useLayoutEffect, интерфейс которого полностью совпадает с useEffect, но по очередности выполнения полностью совпадает с componentDidMount.

Мысли в слух

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

Надеюсь данная статья помогла некоторым из вас восполнить пробел, на который вечно не хватает времени ;-)

Источник: habr.com
К списку статей
Опубликовано: 15.12.2020 06:18:19
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Javascript

Reactjs

React hooks

React

Useeffect

Uselayouteffect

Componentdidmount

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru