5. React правила
5.1. Декомпозиция
- Давайте условимся, что компоненты не должны быть размером более 300 строк кода. Лучше, конечно, меньше.
- Лучше передробить, чем недодробить
5.2. Структура компонент
Чтобы все файлы компонентов были согласованы, следуйте следующей схеме:
// 1. Imports
import { useEffect, useState } from 'react'
// 2. Types
type Props = {
todolist: TodolistDomain
tasks: Task[]
}
// 3. Additional variables
// Если константа используется только в данной компоненте, то выносим ее за пределы компонента.
// Если используется во многих местах приложения, то выносим ее в `common/constants`
const SOME_CONSTANT = 'something'
// 4. Деструктуризация пропсов
export const Todolist = ({ todolist, tasks }: Props) => {
// ❗ Данную логику лучше выносить в кастомный хук
// 4.1. Объявление переменных
// 4.2. Разделяем логически связанные части
const [state, setState] = useState(false)
const [user, setUser] = useState<string | null>(null)
const { fetchTasks, addTask } = useActions(tasksThunks)
const { token } = useParams<{ token?: string }>()
// 4.3. Effects (нету строгого правила)
// * useEffect можно перенести вниз (перед TSX)
useEffect(() => {
fetchTasks(todolist.id)
}, [])
// 4.4. Функции
const addTaskCallBack = (title: string) => {
return addTask({ title, todolistId: todolist.id }).unwrap()
}
// 4.5. Функции обработчики называем с handler в конце
const testHandler = () => {
// code
}
return (
<>
<TodolistTitle todolist={todolist} />
<AddItemForm addItem={addTaskCallBack} />
<Tasks todolist={todolist} tasks={tasks} />
<FilterTasksButtons todolist={todolist} />
<button onClick={testHandler}>test</button>
</>
)
}5.3. Типизация пропсов
Для типизация Props не используем React.FC
import { FC } from 'react'
type Props = {
message: string
}
const Component: FC<Props> = ({ message }) => {
return <>TSX</>
}type Props = {
message: string
}
const Component = ({ message }: Props) => {
return <>TSX</>
}5.3.1. Теория
Что React.FC добавляет в функциональный компонент ?
- явно указывает возвращаемое значение (
ReactNode) - добавляет типы для статический свойств (
displayName,propsTypes,defaultProps)
type FC<P = {}> = FunctionComponent<P>
interface FunctionComponent<P = {}> {
(props: P, context?: any): ReactNode;
propTypes?: WeakValidationMap<P> | undefined;
contextTypes?: ValidationMap<any> | undefined;
defaultProps?: Partial<P> | undefined;
displayName?: string | undefined;
}5.3.2. Минусы
- Лишний импорт (
import { FC } from 'react') - Приходится писать дополнительный дженерик, дополнительная “сущность” в коде
- В современных руководствах от него отказались (пруфы ниже)
5.3.3. Плюсы
React.FCтипизирует возвращаемое значение какReactNode(поможет, если вы захотите вернуть из компонента объект)- Явно показывает, что функция является React component
5.3.4. Статьи на данную тему:
5.4. Кастомный хук (opens in a new tab)
❗ Если логики в компоненте больше 20 строк выносим логику в кастомный хук
Кастомный хук - это функция, которая обычно начинается с префикса "use" (например, useCustomHook), и она может содержать в себе логику состояния, эффектов (side-effects), или другую функциональность, которую вы хотите использовать в ваших компонентах.
❗Преимущества использования кастомных хуков:
- Повторное использование логики. Когда вы создаете кастомный хук, вы можете использовать его в нескольких компонентах, что позволяет избежать дублирования кода.
- Сокрытие деталей. Кастомный хук позволяет сокрыть детали реализации логики внутри него, делая код компонентов более чистым и понятным.
- Упрощение компонентов. Используя кастомный хук, вы можете вынести сложную логику из компонента, делая его более простым и легко поддерживаемым.
const Login = () => {
const isLoggedIn = useSelector(selectIsLoggedIn);
const {formik} = useLogin();
if (isLoggedIn) {
return <Navigate to = {"/"} />;
}
return (
<div>JSX</div>
);
}5.5. Fragment (opens in a new tab)
Если, компонент на верхнем уровне не требует стилизации, то для того, чтобы не создавать лишнюю обертку используем Fragment (...)
// ❌
const ButtonList1 = () => {
return (
<div>
<button>add</button>
<button>remove</button>
</div>
)
}
// ✅
const ButtonList2 = () => {
return (
<>
<button>add</button>
<button>remove</button>
</>
)
}5.6. Деструктуризация пропсов
Деструктурируем пропсы, чтобы писать меньше кода в разметке
// ❌
const Button1 = props => {
return <button>{props.text}</button>
}
// ✅
const Button2 = ({ text }) => {
return <button>{text}</button>
}5.7. Дробим логические выражения
Если логическое выражение составное, то дробим его
// ❌
return (
<>
{(requiredAnswers && !!avatar && !isReminderOpen) || isHelp2Debug && <NavBar>}
</>
)
// ✅
const navbarEnabled = (requiredAnswers && !!avatar && !isReminderOpen) || isHelp2Debug
return (
<>
{ navbarEnabled && <NavBar>}
</>
)5.8. Оптимизация приложения
⭕ (in progress) Избыточная оптимизацию (useMemo, useCallback и пр.)
Передозировка useMemo (opens in a new tab)
5.9. Включайте только один React компонент в файл.
// ❌ Пример плохого использования. В одном файле 3 компонента
//1️⃣
const Component1 = ({ text }: Props) => {
return <div>{text}</div>
}
// 2️⃣
const Component2 = ({ count }: Props) => {
return <div>Count: {count}</div>
}
// 3️⃣
const User = () => {
return (
<div>
<Component1 text={'lorem ipsum'} />
<Component2 count={'10'} />
</div>
)
}const User = () => {
return (
<div>
<Component1 text={"lorem ipsum"}/>
<Component2 count={"10"}/>
</div>
);
};