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

Автор оригинала: Lorenz Weiß
  • Перевод
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%.


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

Quarkly
Делаем процесс создания сайтов и приложений проще

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

    +1
    Спасибо за статью, было полезно! Ждём цикл статей по React)
      0
      Переводные точно будем продолжать время от времени публиковать + не переводами едиными :)
      +1
      Четвёртый пункт, честно говоря, не понял. То ли проблема надуманная, то ли пример плохой. По факту в «решении» просто вместо выноса логики отображения в отдельный компонент заинлайнили эту логику в родителя. Как это упростило тестирование или повторное использование — сказать сложно. Как раз наоборот, если компонент headerInner использовался более чем в одном месте, теперь везде нужно будет прописывать условие показа руками.
        +5

        Двойственные впечатления о статье. Автор запутал сам себя.


        Пример:


          const fetchData = useCallback(() => {
            /*  Calling the api */
          }, []);
        
          useEffect(() => {
            fetchData();
          }, [fetchData]);

        Зачем здесь useCallback? Мы его передаём ниже в какой-нибудь memo-компонент? Нет. От его изменения что-нибудь зависит? Да, useEffect зависит. Ок, а зачем он от него зависит? В качестве dependencies для useCallback указано [], т.к. это по сути статичный метод, он будет одним и тем же всегда. Тогда зачем мы указываем его в качестве зависимости в useEffect? Он ведь никогда не изменится. Далее, зачем нам вообще его определять как отдельный метод, если, тут вот прямо внезапное откровение, вместо того чтобы заниматься этой ерундой, мы можем тело этого метода целиком запихать в его useEffect:


          useEffect(() => {
            /*  Calling the api */
          }, []);

        П1. Ок, разумный довод. Многие не знают, что useRef не только для ref. Также многие не знают что useState часто хорошая альтернатива для ref, вместо useRef. Главное понимать — от ваших данных зависит рендер, если нет то useRef хороший кандидат, если да, то useState ваш друг.


        П2. Не про React
        П3: То же самое, не нужен вам там useCallback.
        П4. Мало, что изменилось, но в целом ок )

          0

          По useCallback. Я тоже подумал, что автор будет где-то ещё использовать метод. И ещё момент: если компонент ре-рендерится, то useCallback не статичен, так?

            0
            В этом и смысл useCallback что при ререндере он вернет ту же самую функцию (referential equality), что и до (если зависимости не изменились)
              0

              Хорошее уточнение про зависимости. Тут автор похоже автоматически, по привычке использует паттерн, при котором функция остаётся неизменной, но только при неизменных зависимостях (пропсах).

                0
                Скорее депсы :-) Под зависимостями я имел вот что: useCallback(функция, [… зависимости])
                  0
                  Это я и имел в виду. :) Зависимости в useCallback
        • НЛО прилетело и опубликовало эту надпись здесь
            +3
            Да, в реакте необычайно легко выстрелить себе в ногу и перерисовать полсайта, когда вам нужно перерисовать единственную нажатую кнопку в его углу. Это явно вытекает из концепции реактовского VDOM, которая очень «тупая» и работает исключительно по referential equality. Изменилась где-то ссылка — ну всё, перерисовываем.

            Поэтому и инструменты, которые этот механизм заменяют на что-то своё (обычно в составе стейт менеджмента) — что редакс, что MobX, да и другие есть — они крайне популярны. Непосредственно рендером реакт занимается довольно неплохо, и неожиданностей (случающихся в менее популярных библиотеках для компонентной отрисовки) в нем уже практически нет. А вот со всем остальным кроме самой отрисовки связываться — себя не любить.
              +2
              Про какую утечку памяти идет речь? Максимум чем грозит первый пример это лишним сравнением виртуального DOM, самой дорогой операции по перерисовке DOM не будет. Про ваш пример со снежинками, а как вы хотите без перерисовки менять положение элементов в окне бразуера?
              • НЛО прилетело и опубликовало эту надпись здесь
                  +1
                  Что вы подразумеваете под «перерисовыватся»? Если это дорогая, в плане производительности, операция по отрисовке элементов в DOM, то ее не будет. Если это операция по сравнению элементов в VDOM, то данная операция очень быстрая, в большинстве случаев за нее беспокоиться не стоит. Способом оптимизации из первого примера данной статьи вам придется пользоваться очень редко, если вообще придется.

                  Я думаю вам стоит глубже разобраться в механизме рендеринга в реакте, например из этой статьи.
                +3
                1)react это либа, jsx/tsx это расширение для языка js/ts
                2)ну а как вы хотели что бы снежинки двигались? риторический вопрос, нужно html перерисовать
                +1

                До недавнего времени мир стремился избавиться от мусора колбэков. Смотрю примеры и вижу мусор колбэков). Код представляет собой вызов функций в функции, еще вызов функции, которая подается от родителя(без проверки на undefined). За это любят react с хуками). Если честно все выглядит как дом на курьих ножках)

                  0
                  Абсолютно согласен. Похоже на дань моды — «сейчас популярно стало функциональное программирование, ой давайте перепишем ВСЕ на функциях», а через пять лет такие «ой прочитал случайно книжку про объектно-ориентированное программирование, так удобно, давайте перепишем все на ООП.». И так бесконечно по кругу инкрементируя версии библиотек.
                    0
                    Ну нет, какая мода? Придумали что-то для решения задачи, использовали в рабочих проектах и… на этом не остановились. Решили поделиться, сделали документацию, открыли код, создали коммьюнити, все это выросло в новую специализацию с рабочими местами (с неплохой оплатой). Но и на этом не остановились. А мы просто наблюдаем и решаем, использовать или нет.

                    Если показалось не «вау», значит потребности просто нет.
                      +3
                      Похоже на дань моды — «сейчас популярно стало функциональное программирование


                      ФП стало «модным» потому что неуклонно растет уровень образования в сфере IT: университеты стали давать более адекватные учебные программы, множество онлайн курсов, все больше и больше книг, качество и количество тематических конференций растет из года в год.

                      Все это приводит к тому, что разработчики начинают более осознанно подходить к проблеме выбора инструментов и подходов для решения задачи. Вот тут как раз и появляется ФП, так как это гораздо более фундаментальный подход, позволяющий значительно сократить количество костылей в продакшене. И чем более разработчиков понимает, что возможно идея «инкрементить в рантайме количество лап кошке, унаследованной от животного» не самая оптимальная для решения их задачи — они начинают копать в сторону. В сторону ФП. Со стороны это может показаться как «мода»\«тренд»\«хайп», но все это существует уже многие многие годы.

                      Я не утверждаю, что ФП (или ООП) не имеет минусов и тем более не утверждаю, что для реализации UI приложений — это лучший выбор, поскольку JS довольно специфичен. И отсюда, кстати, выходит, что хуки реализованы через какие-то грязные хаки на мой взгляд.

                      Что-то я растекся мыслью по древу… примеры в статье — это только примеры. Если вы фигачите загрузку данных и прочий бизнес в компонентах, то ваш код очень сильно пахнет, не зависимо от реакта. И callback-hell был еще задолго до того, как ФП стало набирать популярность: в nodejs, например.

                      И да, использование функций не конвертирует магическим образом вашу программу в ФП-парадигму :)

                        0
                        Если вы фигачите загрузку данных и прочий бизнес в компонентах, то ваш код очень сильно пахнет, не зависимо от реакта. И callback-hell был еще задолго до того, как ФП стало набирать популярность: в nodejs, например.

                        Если этот бизнес будет относиться только к этому конкретному компоненту, зачем его выносить куда-то?
                          +1

                          Мухи отдельно, котлеты отдельно. Реакт (теоретически) это про отобразить. А посчитать это уже что-то другое. И вот то за что мне реакт не нравится, это то что интеграция с посчитать делается очень не очень. В том же ангуляре эта задача решается гораздо удобнее.

                          0
                          ФП = уровень в ИТ. ФП снижает костыли в продакшене. Странные утверждения). Я конечно не эксперт по теории ФП, но ФП также легко может генерировать костыли и проблемы поддержки кода. Это просто другая парадигма построения приложения. Где-то в плюс, где-то в минус. Лично мне сложно представить, чтобы на основе ФП были полноценные современные приложения, особенно ui. Без контекста увы не получится. Наличие контекста — это уже не ФП.
                            0
                            Где-то в плюс, где-то в минус. Лично мне сложно представить, чтобы на основе ФП были полноценные современные приложения, особенно ui.


                            Полностью согласен, что каждый из подходов имеет и плюсы и минусы, о чем я и упоминал в своем комменте. И я тоже считаю, что скорее всего, чистое ФП — не лучший выбор для UI. По крайней мере потому, что рантайм таких приложений не очень предназначен для чистого ФП кода.

                            но ФП также легко может генерировать костыли и проблемы поддержки кода


                            Я может не точно выразился, но имелось ввиду, что при одинаковых ресурсозатратах, ФП на выходе дает более качественный код. Коротко говоря: из-за абстракций. На абстрактный код накладывается много ограничений, что уменьшает количество возможных реализаций. Очень часто можно услышать фразу на подобии: «По сути есть только единственный вариант, корректно реализовать эту функцию» — это означает, что ограничения на типы сделаны так, что разработчик может написать «единственную» реализацию, которая скомпилируется (и попадет в прод).

                            Наличие контекста — это уже не ФП.


                            В корне неверно. Контекст есть и прекрасно описывается и далее ложится на все существующие (в мире ФП) абстракции. И мутабельность тоже: мутирование данных = сайд эффект.

                            А вот в ООП все как раз очень плохо с контекстом: регулярно можно встретить код, который работает только в определенном окружении: нужно чтобы в мутабельных переменных находились «определенные» значения, что очень легко поломать при следующем же рефакторе. Очень простенький пример: есть некий класс, который отвечает за коннекшен к БД и прежде чем делать запросы (получать соединение) — нужно его проинициализировать. Из этого следует, что просто поменяв две строчки местами, мы можем получить баг, о котором узнаем только в рантайме.

                              0
                              Не могли бы скинуть информацию про контекст в ФП. Читая про ФП пишут, что основана на чистых функциях. А наличие контекста если не ООП, то это процедурное программирование
                      0
                      Хуки это вообще худшее что есть в React'e, на самом-то деле.

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

                      Причём вариантов, как именно использовать всё это добро, появляется такое множество, что сейчас, фактически, нет никаких единых стандартов «хорошего кода на React».

                      Что будет, когда разработчики упорются и накидают ещё новых фич, а половину старых объявят деприкейтед — страшно подумать.
                        0
                        Слава богам, классы пока работают и deprecated не объявляются.
                        0
                        Чем больше читаю про хуки, тем больше задаюсь вопросом: зачем надо было уходить от понятных в большинстве случаев даже непосвященному методов lifecycle? Они более функциональны? Да вроде бы все то же самое можно было и раньше делать. Более чистые? Я уже видел мешанину из хуков. Нужно постоянно следить, что бы они были упорядочены. Плюс хуки дают иллюзию того, что у нас stateless-function, что совсем не так. Логика смешивается с представлением. Плюс есть вопросы по производительности: как минимум раньше создание новых объектов в рендере и передача их потомкам — было анти-паттерном из-за лишнего ререндера. Возможно хуки и не создают функции повторно, тут нужно изучить вопрос. Можно конечно сказать, мол какая разница, там копейки расходуются, но рано или поздно на таких копейках можно и разориться.
                        Я люблю реакт, со всеми его плюсами и минусами, но считаю хуки ошибкой. Надеюсь, что ошибаюсь, так как сам я ведь ни одной библиотеки подобной не написал. Возможно мне следует изучить их лучше. Но на данный момент выглядит все это, как, простите, «маркетинговый булщит». Или может быть это просто подготовка к переходу на Recoil? А после recoil на еще какой-нибудь инструмент от фейсбука.
                          0

                          Мне кажется, сначала это было сделано для лаконичности. Функции просты, красивы и удобны, но не поддерживают состояние. Появился useState. Потом, внезапно, люди вспомнили что функциональные компоненты рендерятся на каждый чих, и создаются новые колбеки, которые заставляют перерендериваться даже нормальные компоненты. Так появился useCallback. Ну, а потом кто-то просто не смог остановиться.

                            +6

                            Хуки были созданы для решения проблемы переиспользования логики привязанной к жизненному циклу компонента. До этого использовали HOC и Render props, а ещё раньше — миксины. Проблема HOC — общее пространство имен пропсов, а render props засоряют дерево компонентов и всё это не удобно композировать.


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


                            У хуков конечно есть и свои проблемы, но они решили определенный класс проблем.


                            Официальная мотивация

                              0

                              Cпасибо, интересно. Первые два пункта многое объясняют.


                              А вот это выглядит странно:


                              Classes confuse both people and machines
                              You have to understand how this works in JavaScript
                              You have to remember to bind the event handlers

                              И классы удобны (хотя писать больше, это да), и работа с this это база для любого JS разработчика, и можно не bind-ить если использовать стрелочные функции.

                                0

                                Да, я согласен, это довольно слабый аргумент, по сравнению с остальными, но я так понимаю, это основано на отзывах. Хуки заменили сложности с this на замыкания и ссылочное равенство. Ещё часть про "Classes confuse both people and machines" относится к проблемам с минификаторами и typescipt'ом. Поля классов не могут быть минифицированы без проблем и вывод типов хромал, особенно с HOC.

                                0
                                Я тоже думаю, что автор хуков просто искал способ добавить состояние в функциональные компоненты, как сказали выше. А уже потом обнаружилось, что с ними можно и логику переиспользовать. Иначе в компонентах на классах реализовали бы аналогичное переиспользование логики.

                            0
                            Самое простое решение — установить функцию onSuccess туда, где вызов будет успешным.
                            И рано или поздно получить ошибку в консоль:
                            Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
                            А дальше начнутся костыли с cancelable promise или отменой http запросов, которые в реальных приложениях находятся где-нибудь в action'ах, а не в теле компонента.
                            React — это про состояние и реакцию на его изменение. И изначальный вариант, который у вас «так делать нехорошо», это как раз оптимальный подход, не создающий дополнительных проблем и вписывающийся в парадигму реакта.

                            useEffect(() => {
                              if (success) {
                                onSuccess();
                              }
                            }, [success, onSuccess]);
                              0
                              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>
                                );
                              }


                              :facepalm:
                              Тот случай, когда статью для перевода выбирал не разработчик, а SMM
                                0
                                Доброго дня. Вы угадали, но что всё же не так? Для общего развития :)
                                  0
                                  Доброго.
                                  Конкретно в процитированном месте не так то, что во-первых проблемы-то и не было, а во-вторых решение ничего по сути не изменило)
                                  А в целом не так то, что качественную статью найти всё сложнее, потому что рунет заполонили переводы, которые сделаны с целью посветить ссылкой на свою компанию, статей, которые написаны, чтобы посветить своим именем.
                                    0
                                    Не скажу, что такой цели совсем не было у нас, но к переводу материалов (их выбору) вообще относимся внимательно. Спасибо, что отметили недостатки.

                              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                              Самое читаемое