[JavaScript, ReactJS] В чем разница между useLayoutEffect, componentDidMount, useEffect
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Привет хабр!В 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
===========
Похожие новости:
- [JavaScript, Алгоритмы] Превращаем рекурсию в цикл
- [JavaScript] Разбор понятий: trivial type, standard layout, POD
- [Разработка веб-сайтов, JavaScript, Программирование] JavaScript за 60 секунд: работаем с картой (Geolocation API, Leaflet.js, Nominatim)
- [JavaScript, Node.JS, Amazon Web Services, ReactJS, Облачные сервисы] Serverless шагает по планете. Сравним SberCloud и AWS
- [JavaScript, ReactJS] Эпическая сага про маленький custom hook для React (генераторы, sagas, rxjs) часть 3
- [JavaScript, Программирование, Node.JS] Дино (Deno): Создать API для отдыха с помощью JWT (перевод)
- [JavaScript, ReactJS] Эпическая сага про маленький custom hook для React (генераторы, sagas, rxjs) часть 2
- [JavaScript, Программирование, TypeScript] Кастомизация компонентов Ant Design и оптимизация бандла
- [Python, JavaScript, Браузеры] Brython: заменяем JavaScript на Python на фронтенде (перевод)
- [JavaScript, ReactJS] Эпическая сага про маленький custom hook для React (генераторы, sagas, rxjs)
Теги для поиска: #_javascript, #_reactjs, #_react_hooks, #_react, #_useeffect, #_uselayouteffect, #_componentdidmount, #_javascript, #_reactjs
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 21-Ноя 21:23
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Привет хабр!В 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> ) } } sleep(duration) {
const start = new Date().getTime(); let end = start; while(end < start + duration) { end = new Date().getTime(); } } componentDidMount() {
this.sleep(3000); this.setState({ width: this.ref.current.clientWidth }); } 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> ) } ДокументацияТеперь мы точно знаем, что componentDidMount отличается от useEffect. Чтобы понять, это отличие вызвано просто особенностями реализации хуков или же умышленно, давайте как всегда обратимся к документации. Из этого можно сделать вывод, что React разработчики умышленно дали нам, абсолютно новый API, позволяющий отрисовать контент в браузере до того как запуститься функция переданная в useEffect. Этот API просто не существует при написании компонента на классах. Его можно сымитировать разве что использовав setTimeout внутри componentDidMount. componentDidMount() {
setTimeout(() => { ... }, 0); } =========== Источник: habr.com =========== Похожие новости:
|
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 21-Ноя 21:23
Часовой пояс: UTC + 5