Pull to refresh

Comments 24

Почему вы в исходном варианте биндите _toggle в конструкторе, а в преобразованном — спокойно передаёте каждый раз новую функцию? Где useCallback?

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

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

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

А ещё проще — ничего не трогать.

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


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

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

UFO just landed and posted this here

Для этого стоит написать новые правила, плагин @putout/plugin-react-hooks, представляет собой базовую реализацию, которую можно развивать для поддержки нужных условий.

UFO just landed and posted this here

PureComponent проигнорирует, а shouldComponentUpdate удалит. Используя приемы описанные в статье, поддержку и того и другого легко добавить.

UFO just landed and posted this here

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

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

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

Is it OK to use arrow functions in render methods? ― Generally speaking, yes, it is OK, and it is often the easiest way to pass parameters to callback functions. If you do have performance issues, by all means, optimize!

Вот что там написано на самом деле. Очень аккуратная формулировка "generally… it is OK". А вы чуть ли не обратное написали. Впрочем практически всякий раз когда я вижу эти слова ("преждевременная оптимизация"), я натыкаюсь на какой-нибудь очередной набор отмаз, почему стоит писать O(n^2) или O(n!) вместо O(1) :)


А по сути, если вдаваться в детали: пока вы оперируете только immutable data имеет смысл использовать useMemo\Memo\useCallback как раз таки везде или почти везде. Прямо если возникает желание НЕ писать их, то надо найти достаточные для этого основания. В купе с hooks.macro разница между быстрым кодом и говмедленным кодом это разница в 1 импорт и пару символов.

UFO just landed and posted this here

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


Вы забываете, что есть еще кастомные хуки, вроде описанного Деном Абрамовым useInterval, в них можно сложить многие описанные вами проблемы, такие как useEffect, useMemo и прочее, взгляните на примеры с usehooks. Опять же, статья не о том, зачем нужны хуки, а том как автоматизировать рефакторинг, на примере хуков.

UFO just landed and posted this here
А можно ли назвать такой подход автоматизацией, если в обязательном порядке нужно проверять код?

Да можно.


плагин @putout/plugin-react-hooks, представляет собой базовую реализацию, которую можно развивать для поддержки нужных условий.

Если вас заинтересовала тема автоматизированного рефакторинга, вы так же можете написать интересующие правила, пул реквесты приветствуются.

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

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

Автоматизированный процесс и автоматический процесс — немного разные вещи. Второй — это полная автоматизация.
UFO just landed and posted this here
У вас есть выбор: используйте хуки для простых компонентов, и классы для сложных
Ладно, если в одном проекте классы, а в другом хуки. Но когда в одном и то, и другое, это уже не хорошо. Уж лучше что-то одно.

Ну и пример с классами слишком утрированный. Можно ведь проще (по крайней мере с babel):
class Button extends Component {
     state = { 
         enabled: true,
     };

     toggle = () => {
         this.setState({
             enabled: false,
         });
    };

    render() {
        return (
            <button
                enabled={this.state.enabled}
                onClick={this.toggle}
            />
        );
    }
}

Но может вы просто для примера так класс раздули. Но об этом не сказано.

ИМХО, нет резона использовать для сложных компонент — классы. Всё решается и на хуках (кроме error boundaries, но это вроде бы временно).

В каждом более-менее сложном компоненте придётся тащить как минимум useState, useCallback, useMemo, useEffect

По опыту в пару месяцев работы с хуками (изначально относился к ним скептически): тащить сразу по 3-4 не приходится. Проще получается с композицией. Все эти use* в итоге группируются в отдельные новые use-hook-и и тот самый изначальный сложный класс-компонент превращается в несколько раздельных методов с ограниченной областью задач у каждого. На самом деле мне это показалось куда более удобным, чем возня с class components life cycle методами.


Правда некоторое время нужно попрактиковаться. Скажем я открыл для себя необычное применение для useRef. Не для DOM-элементов, а для хранения того, что раньше было class instance variables.


По большому счёту вся нынешняя инфраструктура React построена так, чтобы было удобно дробить функциональности на мелкие несвязанные составляющие.

Sign up to leave a comment.

Articles