Комментарии 24
Для простоты изложения и наглядности, из всего что касается React Hook'ов
использовалась лишь функция useState
. useCallback
можно будет добавить в будущем. Статья описывает способ, с помощью которого это можно реализовать, а архитектура плагина состоящая из достаточно простых правил к этому стимулирует.
Все-таки, гораздо проще оптимизировать конкретные места в полученном после преобразования коде, чем вручную переписывать компонент-за-компонентом. Ничто не мешает ознакомится с обработанным кодом, внести необходимые правки, и улучшить инструмент, добавив в него недостающие правила, что бы другие пользователи не попадали на те же грабли. Опять же, пример в статье иллюстрационный.
Пример, описанный в статье — иллюстративный. Иногда выход новой мажорной версии библиотеки ломает обратную совместимость, и тогда варианта два:
- использовать старую, неподдерживаемую, в которой могут быть уязвимости;
- в ручную править все несовместимые места;
Я же предлагаю третий вариант: автоматизировать. Я согласен, что React Hooks
, это не мажорное изменение, и можно ничего не трогать. Ну так вас никто и не заставляет. Если все устраивает в старом подходе, а новый не подходит, конечно, ненужно ничего менять. Но если так получается, что не устраивает, то почему бы не автоматизировать переход? И использовать однородные компоненты по всей кодовой базе, а не какие придется.
Для этого стоит написать новые правила, плагин @putout/plugin-react-hooks, представляет собой базовую реализацию, которую можно развивать для поддержки нужных условий.
PureComponent
проигнорирует, а shouldComponentUpdate
удалит. Используя приемы описанные в статье, поддержку и того и другого легко добавить.
На данном этапе разработки в любом случае стоит просмотреть результат выполнения преобразования, конечно не стоит слепо комитить и деплоить приложение. Попробуйте установить 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 импорт и пару символов.
Зато маленькие и простые компоненты, вроде описанного в статье, упрощаются в разы. У вас есть выбор: используйте хуки для простых компонентов, и классы для сложных. В самом по-себе наличии выбора нет проблемы, проблема в том, как им распоряжаться.
Вы забываете, что есть еще кастомные хуки, вроде описанного Деном Абрамовым useInterval, в них можно сложить многие описанные вами проблемы, такие как useEffect
, useMemo
и прочее, взгляните на примеры с usehooks. Опять же, статья не о том, зачем нужны хуки, а том как автоматизировать рефакторинг, на примере хуков.
А можно ли назвать такой подход автоматизацией, если в обязательном порядке нужно проверять код?
Да можно.
плагин @putout/plugin-react-hooks, представляет собой базовую реализацию, которую можно развивать для поддержки нужных условий.
Если вас заинтересовала тема автоматизированного рефакторинга, вы так же можете написать интересующие правила, пул реквесты приветствуются.
По-хорошему здесь нужно было остановиться с ошибкой, потому что утилита не имеет права сама удалять неизвестный ей код.
Вот код, который отвечает за конвертацию класса в функцию, если вам нужен инструмент, о котором мы сейчас говорим, и хуки вас интересуют, можете добавить нужную вам обработку. Код открыт, это опен сорс. В статье описано что и как делать :).
У вас есть выбор: используйте хуки для простых компонентов, и классы для сложныхЛадно, если в одном проекте классы, а в другом хуки. Но когда в одном и то, и другое, это уже не хорошо. Уж лучше что-то одно.
Ну и пример с классами слишком утрированный. Можно ведь проще (по крайней мере с babel):
class Button extends Component {
state = {
enabled: true,
};
toggle = () => {
this.setState({
enabled: false,
});
};
render() {
return (
<button
enabled={this.state.enabled}
onClick={this.toggle}
/>
);
}
}
Но может вы просто для примера так класс раздули. Но об этом не сказано.
В каждом более-менее сложном компоненте придётся тащить как минимум useState, useCallback, useMemo, useEffect
По опыту в пару месяцев работы с хуками (изначально относился к ним скептически): тащить сразу по 3-4 не приходится. Проще получается с композицией. Все эти use* в итоге группируются в отдельные новые use-hook-и и тот самый изначальный сложный класс-компонент превращается в несколько раздельных методов с ограниченной областью задач у каждого. На самом деле мне это показалось куда более удобным, чем возня с class components life cycle методами.
Правда некоторое время нужно попрактиковаться. Скажем я открыл для себя необычное применение для useRef. Не для DOM-элементов, а для хранения того, что раньше было class instance variables.
По большому счёту вся нынешняя инфраструктура React построена так, чтобы было удобно дробить функциональности на мелкие несвязанные составляющие.
Автоматизируем переход на React Hooks