---
title: Лучшие практики
icon: star
---
Этот документ описывает лучшие практики, которых следует придерживаться при работе с фронтендом.
## Управление состоянием
React и Jotai отвечают за управление состоянием в коде.
### Используйте атомы Jotai для хранения состояния
Полезно создавать столько атомов, сколько вам нужно для хранения состояния.
Лучше использовать дополнительные атомы, чем пытаться быть слишком кратким с передачей свойств.
```tsx
import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState';
import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
export const myAtomState = createAtomState({
key: 'myAtomState',
defaultValue: 'default value',
});
export const MyComponent = () => {
const [myAtom, setMyAtom] = useAtomState(myAtomState);
return (
setMyAtom(e.target.value)}
/>
);
}
```
### Не используйте `useRef` для хранения состояния
Избегайте использования `useRef` для хранения состояния.
Если вы хотите сохранять состояние, используйте `useState` или атомы Jotai с `useAtomState`.
Смотрите [как управлять повторными рендерами](#managing-re-renders), если вы считаете, что вам нужен `useRef`, чтобы предотвратить их.
## Управление повторными рендерами
Управлять повторными рендерами в React может быть сложно.
Вот некоторые правила, которые стоит соблюдать, чтобы избегать ненужных повторных рендеров.
Помните, что вы всегда можете избежать повторных рендеров, если поймете причину их возникновения.
### Работа на корневом уровне
Избежать повторных рендеров в новых функциях стало проще, устранив их на корневом уровне.
Сайдкар-компонент `PageChangeEffect` содержит всего один `useEffect`, который реализует всю логику при изменении страницы.
Таким образом, вы знаете, что есть только одно место, которое может вызвать повторный рендер.
### Всегда думайте дважды, прежде чем добавлять `useEffect` в ваш код
Повторные рендеры часто вызываются ненужными `useEffect`.
Подумайте, нужно ли вам использовать `useEffect`, или же вы можете перенести логику в функцию обработчика событий.
Как правило, несложно перенести логику в функции `handleClick` или `handleChange`.
Вы также можете найти их в библиотеках, таких как Apollo: `onCompleted`, `onError` и т. д.
### Используйте дополнительный компонент для извлечения `useEffect` или логики получения данных
Если вы считаете, что нужно добавить `useEffect` в корневой компонент, стоит рассмотреть возможность его извлечения в сайдкар-компонент.
Вы можете применять то же самое для логики получения данных, с хуками Apollo.
```tsx
// ❌ Плохо, вызовет повторные рендеры, даже если данные не меняются,
// потому что useEffect нужно повторно вычислять
export const PageComponent = () => {
const [data, setData] = useAtomState(dataState);
const [someDependency] = useAtomState(someDependencyState);
useEffect(() => {
if(someDependency !== data) {
setData(someDependency);
}
}, [someDependency]);
return
{data}
;
};
export const App = () => (
);
```
```tsx
// ✅ Хорошо, не вызовет повторные рендеры, если данные не меняются,
// потому что useEffect пересчитывается в другом соседнем компоненте
export const PageComponent = () => {
const [data, setData] = useAtomState(dataState);
return
{data}
;
};
export const PageData = () => {
const [data, setData] = useAtomState(dataState);
const [someDependency] = useAtomState(someDependencyState);
useEffect(() => {
if(someDependency !== data) {
setData(someDependency);
}
}, [someDependency]);
return <>>;
};
export const App = () => (
<>
>
);
```
### Используйте состояния семейства атомов и селекторы
Состояния семейства атомов и селекторы — отличный способ избежать повторных рендеров.
Они полезны, когда нужно хранить список элементов.
### Не следует использовать `React.memo(MyComponent)`
Избегайте использования `React.memo()`, так как это не решает причину повторного рендера, но разрывает цепочку рендера, что может привести к неожиданному поведению и усложнить рефакторинг кода.
### Ограничьте использование `useCallback` или `useMemo`
Часто это не нужно и делает код труднее читаемым и поддерживаемым для улучшения производительности, которую сложно заметить.
## Console.logs
`console.log` полезны во время разработки, предоставляя информацию в реальном времени о значениях переменных и потоке кода. Однако оставление их в коде для продакшена может привести к нескольким проблемам:
1. **Производительность**: Избыточное логирование может повлиять на производительность, особенно в клиентских приложениях.
2. **Безопасность**: Логирование конфиденциальных данных может раскрыть критическую информацию тем, кто может просматривать консоль браузера.
3. **Чистота**: Заполнение консоли логами может скрыть важные предупреждения или ошибки, которые нужно увидеть разработчикам или инструментам.
4. **Профессионализм**: Конечные пользователи или клиенты, проверяющие консоль и видящие множество логов, могут усомниться в качестве и обработке кода.
Убедитесь, что все `console.logs` удалены перед загрузкой кода в продакшен.
## Называние
### Наименование переменных
Названия переменных должны точно описывать их цель или функцию.
#### Проблемы с универсальными именами
Универсальные имена в программировании не идеальны, так как они не хватает определенности, что ведет к неясности и ухудшению читаемости кода. Такие имена не передают цель переменной или функции, делая трудным понимать намерение кода без более глубокого исследования. Это может привести к увеличению времени отладки, повышению вероятности ошибок и трудностям в поддержке и сотрудничестве. Между тем, описательные имена делают код самодокументирующимся и более простым для навигации, улучшая качество кода и продуктивность разработчиков.
```tsx
// ❌ Плохо, использует общее имя, которое неясно передает назначение или содержимое
const [value, setValue] = useState('');
```
```tsx
// ✅ Хорошо, использует описательное имя
const [email, setEmail] = useState('');
```
#### Некоторые слова, которых стоит избегать в названиях переменных
* dummy
### Обработчики событий
Названия обработчиков событий должны начинаться с `handle`, в то время как `on` используется как префикс для наименования событий в пропсах компонентов.
```tsx
// ❌ Bad
const onEmailChange = (val: string) => {
// ...
};
```
```tsx
// ✅ Good
const handleEmailChange = (val: string) => {
// ...
};
```
## Необязательные пропсы
Избегайте передачи значения по умолчанию для необязательного пропса.
**ПРИМЕР**
Возьмите компонент `EmailField`, определенный ниже:
```tsx
type EmailFieldProps = {
value: string;
disabled?: boolean;
};
const EmailField = ({ value, disabled = false }: EmailFieldProps) => (
);
```
**Использование**
```tsx
// ❌ Плохо, передача того же значения, что и значение по умолчанию, не добавляет ценности
const Form = () => ;
```
```tsx
// ✅ Хорошо, предполагается значение по умолчанию
const Form = () => ;
```
## Компонент как пропсы
Постарайтесь максимально передавать неинстанцированные компоненты как пропсы, чтобы дети могли самостоятельно определять, какие пропсы им нужно передать.
Наиболее распространенный пример этого — иконки компонентов:
```tsx
const SomeParentComponent = () => ;
// In MyComponent
const MyComponent = ({ MyIcon }: { MyIcon: IconComponent }) => {
const theme = useTheme();
return (
)
};
```
Чтобы React понял, что компонент является компонентом, нужно использовать PascalCase, чтобы потом инстанцировать его с ``
## Сверление пропсов: минимизируйте его
Сверление пропсов в контексте React — это практика передачи переменных состояния и их сеттеров через многие уровни компонентов, даже если промежуточные компоненты их не используют. Хотя иногда это необходимо, чрезмерное сверление пропсов может привести к:
1. **Уменьшение читаемости**: Отслеживание происхождения пропса или мест, где он используется, может стать запутанным в глубоко вложенной структуре компонентов.
2. **Трудности в обслуживании**: Изменения в структуре пропсов одного компонента могут потребовать изменений в нескольких компонентах, даже если они их не используют напрямую.
3. **Снижение повторного использования компонентов**: Компонент, получающий много пропсов только для передачи их дальше, становится менее универсальным и сложным для повторного использования в разных контекстах.
Если вы считаете, что чрезмерно используете сверление пропсов, обратите внимание на [лучшие практики управления состоянием](#state-management).
## Импорт
При импорте предпочтение стоит отдать указанным псевдонимам, а не полным или относительным путям.
**Псевдонимы**
```js
{
alias: {
"~": path.resolve(__dirname, "src"),
"@": path.resolve(__dirname, "src/modules"),
"@testing": path.resolve(__dirname, "src/testing"),
},
}
```
**Использование**
```tsx
// ❌ Bad, specifies the entire relative path
import {
CatalogDecorator
} from '../../../../../testing/decorators/CatalogDecorator';
import {
ComponentDecorator
} from '../../../../../testing/decorators/ComponentDecorator';
```
```tsx
// ✅ Хорошо, используется указанный псевдоним
import { CatalogDecorator } from '~/testing/decorators/CatalogDecorator';
import { ComponentDecorator } from 'twenty-ui/testing';
```
## Проверка схемы
[Zod](https://github.com/colinhacks/zod) — это проверка схемы для нетипизированных объектов:
```js
const validationSchema = z
.object({
exist: z.boolean(),
email: z
.string()
.email('Email must be a valid email'),
password: z
.string()
.regex(PASSWORD_REGEX, 'Password must contain at least 8 characters'),
})
.required();
type Form = z.infer;
```
## Изменения с нарушением совместимости
Всегда проводите тщательное ручное тестирование перед тем, как продолжить, чтобы гарантировать, что изменения не вызвали перебоев в других местах, поскольку тесты еще не были широко интегрированы.