Доки по разработке
This project is maintained by teniryte
Этот файл собирает базовые принципы работы с формами, обработкой ошибок и структурой приложения на React.
В React существуют два основных подхода к работе с инпутами:
defaultValue, defaultChecked, читаем значение при отправке формы);useState и передаём его через value).Если вы передаёте в input проп value, но не обрабатываете onChange, поле становится “замороженным” — его нельзя редактировать. Поэтому:
defaultValue;defaultChecked;value + onChange.type FormDataShape = {
name: string;
email: string;
role: string;
};
export function SimpleForm() {
// управляемое поле
const [name, setName] = useState('John');
// неуправляемые поля будут прочитаны из FormData
async function handleFormAction(data: FormData) {
const payload: FormDataShape = {
name: data.get('name') as string,
email: data.get('email') as string,
role: (data.get('role') as string) || 'user',
};
console.log('Отправка формы', payload);
}
return (
<form action={handleFormAction}>
<label>
Name:
<input
name="name"
value={name}
onChange={e => setName(e.target.value)}
/>
</label>
<label>
Email:
<input name="email" type="email" required />
</label>
<label>
Role:
<select name="role" defaultValue="">
<option value="" disabled>
Choose role
</option>
<option value="admin">Admin</option>
<option value="user">User</option>
<option value="guest">Guest</option>
</select>
</label>
<input type="hidden" name="userId" value="123" />
<button type="submit">Submit</button>
</form>
);
}
Главная мысль: либо вы контролируете значение через состояние, либо доверяете браузеру и берёте данные из FormData при отправке.
В современных фреймворках (например, Next.js) у формы может быть проп action, принимающий функцию. Она вызывается при отправке формы и получает объект FormData. Это позволяет написать привычный HTML‑формат, но обрабатывать данные в серверной функции без ручной работы с событиями.
async function saveProfile(data: FormData) {
const name = data.get('name') as string;
const email = data.get('email') as string;
// здесь может быть запрос к БД или API
console.log('Сохраняем профиль', { name, email });
}
export function ProfileForm() {
return (
<form action={saveProfile}>
<input name="name" placeholder="Name" />
<input name="email" type="email" placeholder="Email" required />
<button type="submit">Save</button>
</form>
);
}
Error Boundary — это “защитная оболочка” вокруг части дерева компонентов. Если внутри неё произойдёт ошибка во время рендера, жизненного цикла или в дочерних компонентах, вместо падения всего приложения отобразится запасной UI.
Библиотека react-error-boundary предоставляет удобный компонент и хук для этого паттерна.
import { ErrorBoundary, FallbackProps } from 'react-error-boundary';
function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) {
return (
<div role="alert">
<p>Что-то пошло не так:</p>
<pre>{error.message}</pre>
<button onClick={resetErrorBoundary}>Попробовать ещё раз</button>
</div>
);
}
function UnstableComponent() {
if (Math.random() > 0.5) {
throw new Error('Случайная ошибка');
}
return <div>Иногда этот компонент падает</div>;
}
export function PageWithBoundary() {
return (
<ErrorBoundary FallbackComponent={ErrorFallback}>
<UnstableComponent />
</ErrorBoundary>
);
}
Рекомендации:
resetErrorBoundary, чтобы давать пользователю способ “перезагрузить” участок UI.React использует key, чтобы отслеживать элементы в списках и при замене компонента на другой. Если вы передадите новый key, React размонтирует старый компонент и смонтирует новый, полностью сбрасывая его локальное состояние.
Это полезно, когда нужно “обнулить” форму или другой state без ручной очистки.
export function ResettableInput() {
const [key, setKey] = useState(0);
return (
<div>
<input
key={key} // при изменении key input будет пересоздан
placeholder="Введите что-нибудь"
/>
<button onClick={() => setKey(prev => prev + 1)}>
Сбросить поле
</button>
</div>
);
}
Запомнить:
key — это не “индекс” для доступа к элементам, а идентификатор экземпляра компонента;key = полное пересоздание компонента с нуля.Layout-компонент обычно описывает “скелет” страницы: HTML‑обёртку, шрифты, глобальные стили, общие панели (Header, Footer), провайдеры контекста. В нём не должно быть логики конкретных страниц (fetch данных, сложный state и т.п.).
Пример упрощённого layout:
export function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
{/* Здесь можно разместить провайдеры (темы, auth, store) */}
{/* Глобальные элементы: */}
{/* <Header /> */}
{children}
{/* <Footer /> */}
</body>
</html>
);
}
Хорошая практика: чем “тоньше” layout и чем меньше в нём логики, тем легче потом поддерживать приложение и переносить страницы между проектами.