Вы когда-нибудь просили нейросеть сгенерировать React-компонент и получали нечто, что работает, но выглядит так, будто его писал стажёр, который прочитал только половину документации? Я — да, и это нормально. Дело в том, что промты для ИИ — это тоже код. Чем точнее спецификация, тем лучше результат. За последний год я протестировал десятки промтов для генерации React-компонентов: от простых кнопок до сложных виртуализированных таблиц. В этой подборке — десять проверенных шаблонов, которые реально экономят время и не заставляют краснеть на код-ревью.
Базовые промты: фундамент, на котором всё держится
Начнём с простого. Если вы новичок в промпт-инжиниринге, эти три шаблона — ваша отправная точка. Они научат вас правильно формулировать задачу и получать предсказуемый результат.
1. Классический функциональный компонент
Задача: Создать компонент, который принимает пропсы и рендерит простую разметку.
Промт:
Создай функциональный компонент React TypeScript под названием UserCard.
Он принимает пропсы: name: string, email: string, role: 'admin' | 'user'.
Рендерит карточку с именем, email и бейджем роли.
Стилизуй с помощью CSS-модулей.
Пример результата:
import styles from './UserCard.module.css';
interface UserCardProps {
name: string;
email: string;
role: 'admin' | 'user';
}
export const UserCard: React.FC<UserCardProps> = ({ name, email, role }) => {
return (
<div className={styles.card}>
<h3>{name}</h3>
<p>{email}</p>
<span className={role === 'admin' ? styles.admin : styles.user}>{role}</span>
</div>
);
};
Почему это работает: вы явно указали типы пропсов, что предотвращает ошибки на этапе компиляции. Пропс role ограничен значениями, что улучшает автодополнение в IDE.
2. Компонент с хуками состояния и эффектов
Задача: Реализовать компонент с внутренним состоянием и побочными эффектами.
Промт:
Напиши компонент Timer, который показывает секундомер.
Используй useState для хранения секунд и useEffect для интервала.
Добавь кнопки Старт, Пауза, Сброс.
Таймер должен быть в TypeScript с типами для всех функций.
Пример результата:
import { useState, useEffect } from 'react';
export const Timer: React.FC = () => {
const [seconds, setSeconds] = useState(0);
const [isRunning, setIsRunning] = useState(false);
useEffect(() => {
if (!isRunning) return;
const interval = setInterval(() => setSeconds(s => s + 1), 1000);
return () => clearInterval(interval);
}, [isRunning]);
return (
<div>
<p>{seconds} сек</p>
<button onClick={() => setIsRunning(true)}>Старт</button>
<button onClick={() => setIsRunning(false)}>Пауза</button>
<button onClick={() => { setSeconds(0); setIsRunning(false); }}>Сброс</button>
</div>
);
};
Здесь важно, что промт требует очистку интервала в useEffect — это предотвращает утечки памяти.
3. Контекст и провайдер
Задача: Создать глобальное состояние с помощью Context API.
Промт:
Создай ThemeContext и ThemeProvider для управления темой (светлая/тёмная).
Используй useContext в компоненте Button, чтобы менять стили в зависимости от темы.
Типизируй контекст.
Пример результата:
// ThemeContext.tsx
import { createContext, useContext, useState } from 'react';
type Theme = 'light' | 'dark';
interface ThemeContextType {
theme: Theme;
toggleTheme: () => void;
}
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
export const ThemeProvider: React.FC = ({ children }) => {
const [theme, setTheme] = useState<Theme>('light');
const toggleTheme = () => setTheme(t => t === 'light' ? 'dark' : 'light');
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
};
export const useTheme = () => {
const ctx = useContext(ThemeContext);
if (!ctx) throw new Error('useTheme must be used within ThemeProvider');
return ctx;
};
// Button.tsx
import { useTheme } from './ThemeContext';
import styles from './Button.module.css';
export const Button: React.FC = ({ children }) => {
const { theme } = useTheme();
return <button className={styles[theme]}>{children}</button>;
};
Такой подход — стандарт в крупных приложениях, и ИИ легко его воспроизводит, если чётко описать задачу.
Продвинутые промты: когда нужно не просто «работает»
Здесь мы выходим на уровень выше: оптимизация, безопасность, тестирование. Эти промты пригодятся, когда проект уже вышел из стадии прототипа.
4. Мемоизация с React.memo
Задача: Оптимизировать компонент, чтобы избежать лишних ререндеров.
Промт:
Создай компонент ProductCard с пропсами product: Product и onAdd: (id: number) => void.
Оберни в React.memo, чтобы компонент не ререндерился, если пропсы не изменились.
Типизируй Product.
Пример результата:
import React from 'react';
interface Product {
id: number;
name: string;
price: number;
}
interface ProductCardProps {
product: Product;
onAdd: (id: number) => void;
}
export const ProductCard: React.FC<ProductCardProps> = React.memo(({ product, onAdd }) => {
console.log('Рендер ProductCard', product.id);
return (
<div>
<h4>{product.name}</h4>
<p>${product.price}</p>
<button onClick={() => onAdd(product.id)}>Добавить</button>
</div>
);
});
Но помните: React.memo — не серебряная пуля. Если пропсы содержат функции, которые создаются заново при каждом рендере родителя, мемоизация не сработает. Поэтому обязательно используйте useCallback в родительском компоненте.
5. useCallback и useMemo
Задача: Оптимизировать вычисления и стабилизировать ссылки на функции.
Промт:
Создай компонент ShoppingList, который получает items: Item[] и onRemove: (id: number) => void.
Используй useMemo для вычисления общей стоимости и useCallback для onRemove.
Покажи пример использования.
Пример результата:
import { useMemo, useCallback } from 'react';
interface Item {
id: number;
name: string;
price: number;
}
interface ShoppingListProps {
items: Item[];
}
export const ShoppingList: React.FC<ShoppingListProps> = ({ items }) => {
const total = useMemo(() => items.reduce((acc, item) => acc + item.price, 0), [items]);
const handleRemove = useCallback((id: number) => {
// логика удаления
}, []);
return (
<div>
<ul>
{items.map(item => (
<li key={item.id}>
{item.name} — ${item.price}
<button onClick={() => handleRemove(item.id)}>Удалить</button>
</li>
))}
</ul>
<p>Итого: ${total}</p>
</div>
);
};
ИИ часто генерирует похожий код, но не всегда объясняет, почему items должен быть в зависимостях useMemo. Уточните это в промте, чтобы получить полное решение.
6. Код-сплиттинг с React.lazy
Задача: Разбить приложение на части для уменьшения начальной загрузки.
Промт:
Реализуй ленивую загрузку для компонента Dashboard.
Используй React.lazy и Suspense с фолбэком "Загрузка...".
Покажи, как импортировать Dashboard в App.
Пример результата:
import { lazy, Suspense } from 'react';
const Dashboard = lazy(() => import('./Dashboard'));
export const App: React.FC = () => {
return (
<Suspense fallback={<div>Загрузка...</div>}>
<Dashboard />
</Suspense>
);
};
Это особенно полезно для больших приложений. По данным Web.dev, уменьшение размера начального JavaScript на 100 КБ сокращает время загрузки на секунду на мобильных устройствах (источник: web.dev/articles/reduce-javascript-payloads).
Экспертные промты: когда ИИ становится архитектором
Теперь самое интересное: промты, которые заставляют нейросеть думать как старший разработчик. Здесь важны детали, которые обычно упускают из виду.
7. Виртуализация списков с react-window
Задача: Отрисовать большой список без потери производительности.
Промт:
Создай компонент VirtualList с использованием react-window.
Он должен принимать items: string[] и высоту строки.
Используй FixedSizeList.
Пример результата:
import { FixedSizeList as List } from 'react-window';
interface VirtualListProps {
items: string[];
itemHeight: number;
}
export const VirtualList: React.FC<VirtualListProps> = ({ items, itemHeight }) => {
return (
<List
height={400}
itemCount={items.length}
itemSize={itemHeight}
width="100%"
>
{({ index, style }) => (
<div style={style}>{items[index]}</div>
)}
</List>
);
};
Если вы работаете со списками из тысяч элементов, виртуализация — обязательный шаг. Библиотека react-window — стандарт в индустрии.
8. Кастомный хук для управления формами
Задача: Создать переиспользуемый хук для валидации форм.
Промт:
Напиши хук useForm, который принимает initialValues и validate функцию.
Возвращает values, errors, handleChange, handleSubmit.
Типизируй всё.
Пример результата:
import { useState } from 'react';
interface FormValues {
[key: string]: string;
}
type Errors<T> = Partial<Record<keyof T, string>>;
export const useForm = <T extends FormValues>(
initialValues: T,
validate: (values: T) => Errors<T>
) => {
const [values, setValues] = useState<T>(initialValues);
const [errors, setErrors] = useState<Errors<T>>({});
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setValues({ ...values, [e.target.name]: e.target.value });
};
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
const validationErrors = validate(values);
setErrors(validationErrors);
if (Object.keys(validationErrors).length === 0) {
// отправка данных
}
};
return { values, errors, handleChange, handleSubmit };
};
Этот хук легко переиспользовать в разных формах, и ИИ отлично справляется с такой задачей, если дать чёткое ТЗ.
9. Оптимизация с useTransition
Задача: Сделать интерфейс отзывчивым при тяжёлых обновлениях.
Промт:
Создай компонент SearchResults, который использует useTransition для фильтрации списка.
Показывай индикатор загрузки во время перехода.
Пример результата:
import { useState, useTransition } from 'react';
const allItems = Array.from({ length: 10000 }, (_, i) => `Item ${i}`);
export const SearchResults: React.FC = () => {
const [query, setQuery] = useState('');
const [isPending, startTransition] = useTransition();
const filtered = allItems.filter(item => item.includes(query));
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
setQuery(value);
startTransition(() => {
// Тяжёлая фильтрация происходит здесь
setFilteredItems(allItems.filter(item => item.includes(value)));
});
};
return (
<div>
<input value={query} onChange={handleChange} />
{isPending && <p>Загрузка...</p>}
<ul>
{filtered.map(item => <li key={item}>{item}</li>)}
</ul>
</div>
);
};
useTransition появился в React 18 и позволяет помечать не срочные обновления состояния, сохраняя отзывчивость интерфейса.
10. Компонент с асинхронной загрузкой данных
Задача: Реализовать загрузку данных с сервера с обработкой состояний.
Промт:
Создай компонент UserProfile, который загружает данные по ID с помощью fetch.
Покажи состояния: загрузка, ошибка, успех.
Используй useEffect и AbortController для отмены запроса при размонтировании.
Пример результата:
import { useState, useEffect } from 'react';
interface User {
id: number;
name: string;
email: string;
}
export const UserProfile: React.FC<{ userId: number }> = ({ userId }) => {
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const controller = new AbortController();
setLoading(true);
fetch(`/api/users/${userId}`, { signal: controller.signal })
.then(res => {
if (!res.ok) throw new Error('Ошибка загрузки');
return res.json();
})
.then(data => setUser(data))
.catch(err => {
if (err.name !== 'AbortError') setError(err.message);
})
.finally(() => setLoading(false));
return () => controller.abort();
}, [userId]);
if (loading) return <div>Загрузка...</div>;
if (error) return <div>Ошибка: {error}</div>;
if (!user) return null;
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
};
Обратите внимание на AbortController — это важный паттерн для предотвращения гонок состояний.
Как адаптировать промты под свой проект
Главный секрет — не копировать промты вслепую, а адаптировать. Замените типы, названия и стили под свой контекст. Добавьте требования к тестам, если нужно: «Напиши юнит-тесты с Jest и Testing Library». Укажите версию React, если используете старую — например, React 17 не поддерживает useTransition.
Итоги
Эти десять промтов покрывают путь от простых функциональных компонентов до оптимизированных с мемоизацией и код-сплиттингом. Используйте их как отправную точку — и не забывайте проверять сгенерированный код. ИИ — мощный инструмент, но ответственность за качество всегда лежит на вас. Экспериментируйте, добавляйте свои требования, и вы увидите, как скорость разработки растёт, а количество багов падает.
Комментарии