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

Useeffect

Перевод 5 типичных ошибок при создании React компонентов (с хуками) в 2020 году

01.07.2020 18:14:54 | Автор: admin
image

Всем привет! Из этой статьи вы узнаете о самых распространенных ошибках при создании React компонентов, а также о том, почему они считаются ошибками, как их избежать или исправить.


Оригинальный материал был написан немецким разработчиком Лоренцом Вайсом для личного блога, а позже собрал много позитивных отзывов на dev.to. Переведено командой Quarkly специально для комьюнити на Хабре.



React


React достаточно давно существует в мире веб-разработки, и его позиции как инструмента для гибкой разработки стремительно укрепились за последние годы. А после анонса и релиза нового хука api/concept создание React-компонентов стало ещё проще.


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


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


Дисклеймер


Прежде чем перейти к списку, должен сказать, что на первый взгляд большинство ошибок из списка на самом деле не являются фундаментальными или даже выглядят вполне корректно. Большинство из них вряд ли вообще повлияют на работу или внешний вид приложения. Скорее всего, кроме разработчиков, работающих над продуктом, никто не заметит, что здесь что-то не так. Но я всё равно считаю, что хороший качественный код может помочь улучшить опыт разработки и, следовательно, разработать лучший продукт.


Что касается React-кода, как и по любому другому программному фреймворку или библиотеке, по нему существует миллион различных мнений. Всё, что вы видите здесь, мое личное мнение, а не истина в последней инстанции. Если у вас другое мнение, я с удовольствием выслушаю его.


1. Использование useState, когда нет необходимости в повторном рендере


Одна из основных концепций Reactа это работа с состоянием. Вы можете контролировать весь поток данных и рендеринг через состояние. Каждый раз, когда дерево рендерится снова, оно, скорее всего, привязывается к изменению состояния.


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


Теперь больше о примере: предположим, что у нас есть две кнопки, одна кнопка счетчик, а другая отправляет запрос или запускает действие с текущим счетчиком. Однако текущий номер не отображается внутри компонента. Он требуется только для запроса при нажатии второй кнопки.


Так делать нехорошо:


function ClickButton(props) {  const [count, setCount] = useState(0);  const onClickCount = () => {    setCount((c) => c + 1);  };  const onClickRequest = () => {    apiCall(count);  };  return (    <div>      <button onClick={onClickCount}>Counter</button>      <button onClick={onClickRequest}>Submit</button>    </div>  );}

Проблема:


На первый взгляд, вы можете спросить: А в чем, собственно, проблема? Разве не для этого было создано это состояние? И будете правы: всё отлично сработает, и проблемы вряд ли возникнут. Однако в Reactе каждое изменение состояния влияет на компонент и, скорее всего, на его дочерние компоненты, то есть заставляет их выполнить повторный рендеринг.


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


Решение:


Если внутри вашего компонента вы хотите использовать переменную, которая должна сохранять свое значение между рендерингом, но при этом не вызывать повторный рендеринг, вы можете использовать хук useRef. Он сохранит значение, но не приведет к повторному рендерингу.


function ClickButton(props) {  const count = useRef(0);  const onClickCount = () => {    count.current++;  };  const onClickRequest = () => {    apiCall(count.current);  };  return (    <div>      <button onClick={onClickCount}>Counter</button>      <button onClick={onClickRequest}>Submit</button>    </div>  );}

2. Использование router.push вместо ссылки


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


Допустим, вы создаете кнопку, и пользователь при нажатии на неё должен быть перенаправлен на другую страницу. Так как это SPA, то это действие будет клиентским механизмом маршрутизации. Так что вам понадобится какая-нибудь библиотека. Самая популярная из них в React это react-router, и в следующем примере будет использована именно эта библиотека.


Значит ли это, что добавление слушателя события по клику правильно перенаправит пользователя на нужную страницу?


Так делать нехорошо:


function ClickButton(props) {  const history = useHistory();  const onClick = () => {    history.push('/next-page');  };  return <button onClick={onClick}>Go to next page</button>;}

Проблема:


Хотя для большинства пользователей это вполне рабочее решение, есть одна большая проблема: она появляется, когда дело касается реализации веб-доступности. Кнопка вообще не будет помечена как ссылка на другую страницу, а это значит, что скринридер не сможет ее идентифицировать. Сможете ли вы открыть ее в новой вкладке или окне? Скорее всего, тоже нет.


Решение:


Ссылки на другие страницы при любом взаимодействии с пользователем должны, насколько это возможно, обрабатываться компонентом <Link> или обычным тегом <a>.


function ClickButton(props) {  return (    <Link to="/next-page">      <span>Go to next page</span>    </Link>  );}

Бонусы: это также делает код более читабельным и лаконичным!


3. Обработка действий с помощью useEffect


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


Представьте себе компонент, который извлекает список элементов и выводит их в DOM. Помимо этого, в случае успешного выполнения запроса мы хотим вызвать функцию onSuccess, которая передается компоненту в качестве свойства.


Так делать нехорошо:


function DataList({ onSuccess }) {  const [loading, setLoading] = useState(false);  const [error, setError] = useState(null);  const [data, setData] = useState(null);  const fetchData = useCallback(() => {    setLoading(true);    callApi()      .then((res) => setData(res))      .catch((err) => setError(err))      .finally(() => setLoading(false));  }, []);  useEffect(() => {    fetchData();  }, [fetchData]);  useEffect(() => {    if (!loading && !error && data) {      onSuccess();    }  }, [loading, error, data, onSuccess]);  return <div>Data: {data}</div>;}

Проблема:


Есть два хука useEffect: первый обрабатывает запрос данных к API во время первоначального рендеринга, а второй вызывает функцию onSuccess. То есть, если в состоянии нет загрузки или ошибки, но есть данные, то этот вызов будет успешным. Логично звучит, да?


Конечно, это почти всегда будет срабатывать с первым вызовом. Но вы также потеряете прямую связь между действием и вызываемой функцией. И нет 100% гарантии, что это произойдет только в том случае, если действие fetch будет успешным. А это именно то, что мы, разработчики, так сильно не любим.


Решение:


Самое простое решение установить функцию onSuccess туда, где вызов будет успешным.


function DataList({ onSuccess }) {  const [loading, setLoading] = useState(false);  const [error, setError] = useState(null);  const [data, setData] = useState(null);  const fetchData = useCallback(() => {    setLoading(true);    callApi()      .then((fetchedData) => {        setData(fetchedData);        onSuccess();      })      .catch((err) => setError(err))      .finally(() => setLoading(false));  }, [onSuccess]);  useEffect(() => {    fetchData();  }, [fetchData]);  return <div>{data}</div>;}

Теперь с первого взгляда понятно, что onSuccess вызывается только в случае успешного вызова API.


4. Компоненты с единой ответственностью


Составлять компоненты может быть довольно трудной задачей. Когда делить один компонент на несколько более мелких компонентов? Как структурировать дерево компонентов? Этими вопросами задаются все, кто каждый день работает с компонентным фреймворком. И самой распространенной ошибкой при создании компонентов является объединение двух случаев использования в один компонент.


Возьмем в качестве примера заголовок, который показывает либо кнопку бургерного меню на мобильном устройстве, либо вкладки на десктопе (условие будет обработано волшебной функцией isMobile, которая не является частью этого примера).


Так делать нехорошо:


function Header(props) {  return (    <header>      <HeaderInner menuItems={menuItems} />    </header>  );}function HeaderInner({ menuItems }) {  return isMobile() ? <BurgerButton menuItems={menuItems} /> : <Tabs tabData={menuItems} />;}

Проблема:


При таком подходе компонент HeaderInner пытается вести двойную жизнь. А мы знаем, что тяжело быть Кларком Кентом и Суперменом одновременно. Это также затрудняет процесс тестирования или повторного использования компонента в других местах.


Решение:


Если перенесем условие на один уровень выше, будет проще увидеть, для чего сделаны компоненты, и что они отвечают только за одно: заголовок, вкладку или кнопку бургер-меню, и не стоит пытаться отвечать за несколько вещей одновременно.


function Header(props) {  return (    <header>{isMobile() ? <BurgerButton menuItems={menuItems} /> : <Tabs tabData={menuItems} />}</header>  );}

5. useEffect с единой ответственностью


Помните времена, когда у нас были только методы componentWillReceiveProps или componentDidUpdate для подключения к процессу рендеринга React-компонента? В голову сразу приходят мрачные воспоминания, а ещё осознаешь всю прелесть использования хука useEffect и особенно то, что его можно использовать без ограничений.


Но иногда, когда используешь useEffect для некоторых вещей, мрачные воспоминания приходят вновь. Представьте, например, что у вас есть компонент, который извлекает некоторые данные из бэкэнда и отображает путь (breadcrumbs) в зависимости от текущего местоположения (снова используется react-router для получения текущего местоположения).


Так делать нехорошо:


function Example(props) {  const location = useLocation();  const fetchData = useCallback(() => {    /*  Calling the api */  }, []);  const updateBreadcrumbs = useCallback(() => {    /* Updating the breadcrumbs*/  }, []);  useEffect(() => {    fetchData();    updateBreadcrumbs();  }, [location.pathname, fetchData, updateBreadcrumbs]);  return (    <div>      <BreadCrumbs />    </div>  );}

Проблема:


Существует два варианта использования хука: сбор данных (data-fetching) и отображение пути (displaying breadcrumbs). Оба обновляются с помощью хука useEffect. Этот самый хук useEffect сработает, когда fetchData и updateBreadcrumbs функционируют или меняется location. Основная проблема в том, что теперь мы также вызываем функцию fetchData при изменении location. Это может стать побочным эффектом, о котором мы и не подумали.


Решение:


Если разделить эффект, станет понятно, что функции используются только для одного эффекта, при этом неожиданные побочные эффекты исчезают.


function Example(props) {  const location = useLocation();  const updateBreadcrumbs = useCallback(() => {    /* Updating the breadcrumbs*/  }, []);  useEffect(() => {    updateBreadcrumbs();  }, [location.pathname, updateBreadcrumbs]);  const fetchData = useCallback(() => {    /*  Calling the api */  }, []);  useEffect(() => {    fetchData();  }, [fetchData]);  return (    <div>      <BreadCrumbs />    </div>  );}

Бонусы: случаи использования теперь также логически распределены внутри компонента.


Заключение


При создании React-компонентов натыкаешься на множество подводных камней. Никогда не удается досконально понять весь механизм, и вы обязательно столкнетесь хотя бы с маленьким, а, скорее всего, даже с большим косяком. Однако совершать ошибки тоже нужно, когда изучаешь какой-нибудь фреймворк или язык программирования. Никто не застрахован от них на 100%.


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

Подробнее..

Перевод Мифы о useEffect

07.10.2020 10:05:19 | Автор: admin


Доброго времени суток, друзья!

Представляю вашему вниманию перевод небольшой заметки Kent C. Dodds, в которой он делится своими соображениями относительно правильного использования хука useEffect.

Я обучил React тысячи разработчиков. Как до, так и после релиза
хуков. Одной из вещей, которые я заметил, является не очень четкое понимание
назначения и механизма работы хука useEffect. В этой статье я хочу немного об этом рассказать.

Никакого отношения к стадиям жизненного цикла синхронизация дополнительных эффектов


Разработчики, которые имеют опыт работы с классовыми компонентами и стадиями
жизненного цикла, такими как constructor, componentDidMount,
componentDidUpdate и componentWillUnmount, иногда пытаются реализовать
аналогичное поведение в функциональных компонентах с помощью хуков. Это большая
ошибка. Позвольте мне это продемонстрировать. Вот пример забавного
пользовательского интерфейса:



Вот реализация компонента DogInfo с помощью классов:

  class DogInfo extends React.Component {    controller = null;    state = { dog: null };    fetchDog() {      this.controller?.abort();      this.controller = new AbortController();      getDog(this.props.dogId, { signal: this.controller.signal }).then(        (dog) => {          this.setState({ dog });        },        (error) => {          // обработка ошибок        }      );    }    componentDidMount() {      this.fetchDog();    }    componentDidUpdate(prevProps) {      // обработка изменения dogId      if (prevProps.dogId !== this.props.dogId) {        this.fetchDog();      }    }    componentWillUnmount() {      // отмена запроса      this.controller?.abort();    }    render() {      return <div>{/* рендеринг информации о собаке */}</div>;    }  }

Это стандартный компонент для такого вида интеракции. В нем
используются стадии жизненного цикла constructor, componentDidMount,
componentDidUpdate и componentWillUnmount. Вот что получится, если мы
обернем эти стадии в хуки:

  function DogInfo({ dogId }) {    const controllerRef = React.useRef(null);    const [dog, setDog] = React.useState(null);    function fetchDog() {      controllerRef.current?.abort();      controllerRef.current = new AbortController();      getDog(dogId, { signal: controllerRef.current.signal }).then(        (d) => setDog(d),        (er) => {          // обработка ошибок        }      );    }    // didMount    React.useEffect(() => {      fetchDog();      // eslint-disable-next-line react-hooks/exhaustive-deps    }, []);    // didUpdate    const prevDogId = usePrevious(dogId);    useUpdate(() => {      if (prevDogId !== dogId) {        fetchDog();      }    });    // willUnmount    React.useEffect(() => {      return () => {        controllerRef.current?.abort();      };    }, []);    return <div>{/* рендеринг информации о собаке */}</div>;  }  function usePrevious(value) {    const ref = useRef();    useEffect(() => {      ref.current = value;    }, [value]);    return ref.current;  }

Здесь имеется некоторая несогласованность между хуками. Если бы они
предназначались для использования таким образом, я тоже был бы их противником.
Но правда в том, что useEffect это не стадия жизненного цикла. Это механизм
синхронизации дополнительных эффектов с состоянием приложения. В
приведенном примере наша задача состоит в том, чтобы запрашивать информацию о
собаке при изменении dogId. Учитывая это, useEffect становится намного проще:

  function DogInfo({ dogId }) {    const [dog, setDog] = useState(null);    useEffect(() => {      const controller = new AbortController();      getDog(dogId, { signal: controller.signal }).then(        (d) => setDog(d),        (er) => {          // обработка ошибок        }      );      return () => controller.abort();    }, [dogId]);    return <div>{/* рендеринг информации о собаке */}</div>;  }

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

Запомните высказывание Ryan Florence:
Вопрос не в том, когда запускать эффект, вопрос в том, с каким состоянием
он должен синхронизироваться

useEffect(fn) // все состояния
useEffect(fn, []) // отсутствие состояний
useEffect(fn, [these, states]) // указанные состояния

Могу я игнорировать eslint-plugin-react-hooks/exhaustive-deps?


Ну, технически да. И иногда для этого существуют хорошие причины. Однако, в
большинстве случаев это является плохой идеей, игнорирование этого правила почти наверняка приведет к ошибкам. Обычно, люди после этого говорят: Но я только
хотел, чтобы это запускалось после рендеринга!. Вот опять. Думать о хуках в
категориях жизненного цикла неправильно. Если ваш колбэк в useEffect имеет
зависимость, необходимо убедиться, что он выполняется при каждом изменении
этой зависимости. В противном случае, эффект не будет синхронизирован с
состоянием приложения. Короче говоря, у вас будут проблемы. Не игнорируйте
данное правило.

Один большой useEffect


Честно говоря, я давно такого не видел. Но такое порой все-таки случается. То,
что мне действительно нравится в useEffect, так это возможность разделения задачи на любое количество подзадач. Вот простой пример:



Вот некоторый всевдокод для этого демо:

  class ChatFeed extends React.Component {    componentDidMount() {      this.subscribeToFeed();      this.setDocumentTitle();      this.subscribeToOnlineStatus();      this.subscribeToGeoLocation();    }    componentWillUnmount() {      this.unsubscribeFromFeed();      this.restoreDocumentTitle();      this.unsubscribeFromOnlineStatus();      this.unsubscribeFromGeoLocation();    }    componentDidUpdate(prevProps, prevState) {      // ... сранение пропсов, повторной подписки и т.д.    }    render() {      return <div>{/* интерфейс чата */}</div>;    }  }

Видите эти 4 задачи? Они смешаны. Что, если вы захотите поделиться частью функционала? Я имею ввиду, что пропсы рендеринга отличная вещь, но хуки
все же лучше. Я видел, как некоторые люди создают огромный useEffect, отвечающий
за все:

  function ChatFeed() {    React.useEffect(() => {      // подписка на ленту      // установка заголовка документа      // перевод статуса в онлайн      // определения местоположения      return () => {        // отписка от ленты        // восстановление заголовка        // перевод статуса в офлайн        // отключение определения местоположения      };    });    return <div>{/* интерфейс чата */}</div>;  }

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

  function ChatFeed() {    React.useEffect(() => {      // подписка на ленту      return () => {        // отписка от ленты      };    });    React.useEffect(() => {      // установка заголовка документа      return () => {        // восстановление заголовка      };    });    React.useEffect(() => {      // перевод статуса в онлайн      return () => {        // перевод статуса в офлайн      };    });    React.useEffect(() => {      // определения местоположения      return () => {        // отключение определения местоположения      };    });    return <div>{/* интерфейс чата */}</div>;  }

Самодостаточность хуков дает массу преимуществ.

Внешние функции


Я видел такое несколько раз. Позвольте мне просто привести код до и после:

  // до. Не делайте так  function DogInfo({ dogId }) {    const [dog, setDog] = React.useState(null);    const controllerRef = React.useRef(null);    const fetchDog = React.useCallback((dogId) => {      controllerRef.current?.abort();      controllerRef.current = new AbortController();      return getDog(dogId, { signal: controller.signal }).then(        (d) => setDog(d),        (error) => {          // обработка ошибок        }      );    }, []);    React.useEffect(() => {      fetchDog(dogId);      return () => controller.current?.abort();    }, [dogId, fetchDog]);    return <div>{/* рендеринг информации о собаках */}</div>;  }

Мы уже видели, как можно упростить этот код, но давайте взглянем на него еще раз:

  function DogInfo({ dogId }) {    const [dog, setDog] = React.useState(null);    React.useEffect(() => {      const controller = new AbortController();      getDog(dogId, { signal: controller.signal }).then(        (d) => setDog(d),        (error) => {          // обработка ошибок        }      );      return () => controller.abort();    }, [dogId]);    return <div>{/* рендеринг информации о собаках */}</div>;  }

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

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

Заключение


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

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

15.12.2020 06:18:19 | Автор: admin

Привет хабр!

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

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

Подробнее..

Категории

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

  • Имя: Макс
    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