Размышления о React
Необходимый минимум
Когда машина умнее тебя
- Выполняй статический анализ кода с помощью
ESLint
. Используй плагинeslint-plugin-react-hooks
для перехвата ошибок, связанных с неправильным использованием хуков.
Установка
npm i -D eslint-plugin-react-hooks
# or
yarn add -D eslint-plugin-react-hooks
Настройки eslint
{
"extends": [
// ...
"plugin:react-hooks/recommended"
]
}
Или:
{
"plugins": [
// ...
"react-hooks"
],
"rules": {
// ...
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn"
}
}
При использовании create-react-app
данный плагин включается в проект автоматически.
- Включай строгий режим. На дворе 2022 год, в конце концов.
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
)
-
Не обманывай
React
о зависимостях. Исправляй предупреждения/ошибки, связанные с зависимостями, возникающие при использовании хуковuseEffect
,useCallback
иuseMemo
. Для обеспечения актуальности колбеков без выполнения лишних ререндеров можно использовать такой паттерн. -
Не забывай добавлять ключи при использовании
map
для рендеринга списка элементов. -
Используй хуки только на верхнем уровне. Не вызывай их в циклах, условиях и вложенных функциях.
-
Разберись с тем, что означает предупреждение
Can't perform state update on unmounted component
(невозможно обновить состояние размонтированного компонента). -
Избегай появления белого экрана смерти путем использования предохранителей на разных уровнях приложения. Предохранители можно также использовать для отправки сообщений в сервис мониторинга ошибок, такой как
Sentry
- статья о том, как это сделать. -
Если в консоли инструментов разработчика в браузере имеются предупреждения или ошибки, на это есть причина!
-
Не забывай про
tree-shaking
(тряску дерева). -
Prettier
(или похожие инструменты) избавляет от необходимости заботиться о единообразном форматировании кода за счет автоматизации этого процесса. -
TypeScript
сделает твою жизнь намного проще. -
Для открытых проектов настоятельно рекомендую использовать
Code Climate
. Автоматическое обнаружение "дурно пахнущего" кода мотивирует на борьбу с техническим долгом. -
Next.js
- отличный метафреймворк.
Код - это необходимое зло
"Лучший код - это его отсутствие. Каждая новая строка кода, которую ты написал, это код, который кому-то придется отлаживать, код, который кому-то придется читать и понимать, код, который кому-то придется поддерживать." Jeff Atwood
"Одним из самых продуктивных дней в моей жизни был день, когда я просто удалил 1000 строк кода." Eric S. Raymond
TL;DR
- Думай перед добавлением новой зависимости.
- Пиши код в стиле, характерном для
React
. - Не умничай! YAGNI
Думай перед добавлением новой зависимости.
Чем больше зависимостей в проекте, тем большее количество кода загружается браузером. Спроси себя, действительно ли ты используешь возможности, предоставляемые конкретной библиотекой?
Возможно, тебе это не нужно
-
Действительно ли тебе нужен
Redux
? Может быть. Но не забывай, чтоReact
- сам по себе отличный инструмент для управления состоянием. -
Действительно ли тебе нужен
Apollo Client
?Apollo Client
предоставляет большое количество прикольных "фич". Однако его использование существенно увеличивает размер сборки. Если в приложении используются фичи, которые не являются уникальными дляApollo Client
, попробуй заменить его на такие библиотеки, какSWR
илиreact-query
(https://react-query.tanstack.com/comparison) (или обойтись вообще без библиотек). -
Axios
?axios
- отличная библиотека, возможности которой нелегко реализовать с помощью нативногоfetch
. Но если единственной причиной использованияaxios
является более привлекательный интерфейс, реализуй обертку надfetch
- статья о том, как это сделать. -
Возможно, для "темизации" (режим
light
/dark
) тебе не нужен контекст. Попробуй использовать для этогопеременные CSS
. -
Возм ожно, тебе не нужен
JavaScript
. СовременныйCSS
- очень мощный инструмент. Вам не нуженJavaScript
.
Пиши код в стиле, характерном для React
.
- Избегай сложных условий и используй короткие вычисления (оператор
&&
). - Вместо традиционных циклов (
for
,for in
,for of
и т.д.) используй цепочки из встроенных функций высшего порядка (map
,filter
,reduce
,find
,some
и др.). Но не забывай, что в некоторых случаях традиционные циклы могут быть более производительными.
Не умничай!
"Что произойдет с моим софтом в будущем? Возможно, случится то и это. Давайте сразу это реализуем, раз уж мы все равно разрабатываем эту часть приложения. Это позволит предвосхитить дальнейшее развитие проекта".
Никогда так не делай! Реализуй только те вещи, которые нужны в данный момент. Не считай себя Нострадамусом.
Код, над которым ты работал, должен стать лучше, чем был
Если ты встретил код, который "дурно пахнет", исправь его. Если исправить его сложно или на это нет времени, хотя бы пометь его комментарием (FIXME
или TODO
) с краткой характеристикой проблемы. Пусть все об этом знают. Это покажет другим, что тебе не все равно, а также добросовестное отношение к тому, что ты делаешь.
Примеры "дурно пахнущего" кода:
- ❌ методы или функции принимают большое количество аргументов
- ❌ сложные логические проверки
- ❌ длинные строки кода, помещенные в одну строку
- ❌ синтаксически идентичный дублирующийся код (такой код может иметь разное форматирование)
- ❌ функции или методы, выполняющие много задач
- ❌ классы или компоненты, включающие много функций или методов
- ❌ сложная логика в одной функции или методе
- ❌ функции или методы с большим количеством инструкций
return
- ❌ код с одинаковой структурой (названия переменных могут отличаться)
"Дурно пахнущий" код не обязательно означает, что такой код нужно менять. Это лишь означает, что, скорее всего, ты мог бы реализовать аналогичный функционал лучше.
Ты можешь сделать лучше
setState
(изuseState
) иdispatch
(изuseReducer
) не нужно помещать в массив зависимостей таких хуков, какuseEffect
иuseCallback
, посколькуReact
гарантирует их стабильность.
// ❌
const decrement = useCallback(() => setCount(count - 1), [count, setCount])
const decrement = useCallback(() => setCount(count - 1), [count])
// ✅
const decrement = useCallback(() => setCount((c) => c - 1), [])
- если
useMemo
илиuseCallback
не имеют зависимосте й, скорее всего, ты неправильно их используешь
// ❌
const MyComponent = (props) => {
const fnToCall = useCallback((str) => `hello ${str || 'world'}`, [])
const aConstant = useMemo(() => ({ x: 1, y: 3 }), [])
// ...
}
// ✅
const A_CONSTANT = { x: 1, y: 3 }
const fnToCall = (str) => `hello ${str || 'world'}`
const MyComponent = (props) => {
// ...
}
- оборачивай кастомный контекст в хук: это не только сделает интерфейс лучше, но и позволит ограничиться одним импортом
// ❌
import { useContext } from 'react'
import { SomeContext } from './context'
export default function App() {
const somethingFromContext = useContext(SomeContext)
// ...
}
// ✅
export function useSomeContext() {
const context = useContext(SomeContext)
if (!context) {
throw new Error('useSomeContext может использоваться только внутри SomeProvider')
}
}
import { useSomeContext } from './context'
export default function App() {
const somethingFromContext = useSomeContext()
// ...
}
- думай о том, как компонент будет использоваться, перед тем, как писать код