← Назад к вопросам
Делал ли приложения на JavaScript для взаимодействия с пользователем
1.7 Middle🔥 121 комментариев
#JavaScript Core#Браузер и сетевые технологии
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Интерактивные приложения на JavaScript
Да, у меня обширный опыт создания интерактивных приложений на JavaScript, от простых в браузере до сложных веб-приложений с высокими требованиями к UX.
Типы приложений, которые я создавал
1. Реактивные веб-приложения (React)
Специализируюсь на создании высокопроизводительных приложений с React:
// Пример: Приложение для управления задачами с состоянием
import { useState, useCallback, useMemo } from 'react';
export function TaskManager() {
const [tasks, setTasks] = useState([]);
const [filter, setFilter] = useState('all');
const [searchTerm, setSearchTerm] = useState('');
const addTask = useCallback((title) => {
setTasks(prev => [{
id: Date.now(),
title,
completed: false,
createdAt: new Date(),
}, ...prev]);
}, []);
const toggleTask = useCallback((id) => {
setTasks(prev => prev.map(task =>
task.id === id ? { ...task, completed: !task.completed } : task
));
}, []);
const filteredTasks = useMemo(() => {
return tasks.filter(task => {
const matchesFilter =
filter === 'all' ||
(filter === 'active' && !task.completed) ||
(filter === 'completed' && task.completed);
const matchesSearch = task.title.toLowerCase()
.includes(searchTerm.toLowerCase());
return matchesFilter && matchesSearch;
});
}, [tasks, filter, searchTerm]);
return (
<div className="p-4 max-w-2xl mx-auto">
<h1 className="text-2xl font-bold mb-4">Мои задачи</h1>
<input
type="text"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Поиск задач..."
className="w-full px-4 py-2 border rounded-lg mb-4"
/>
<div className="flex gap-2 mb-4">
{['all', 'active', 'completed'].map(f => (
<button
key={f}
onClick={() => setFilter(f)}
className={`px-4 py-2 rounded ${filter === f ? 'bg-blue-600 text-white' : 'bg-gray-200'}`}
>
{f.charAt(0).toUpperCase() + f.slice(1)}
</button>
))}
</div>
<TaskList tasks={filteredTasks} onToggle={toggleTask} />
</div>
);
}
2. Real-time приложения с WebSocket
Создавал приложения с живыми обновлениями:
// Чат-приложение с WebSocket
import { useEffect, useState, useRef } from 'react';
export function ChatApp() {
const [messages, setMessages] = useState([]);
const [input, setInput] = useState('');
const wsRef = useRef(null);
useEffect(() => {
wsRef.current = new WebSocket('wss://api.example.com/chat');
wsRef.current.onopen = () => {
console.log('Connected to chat');
};
wsRef.current.onmessage = (event) => {
const message = JSON.parse(event.data);
setMessages(prev => [...prev, message]);
};
wsRef.current.onerror = (error) => {
console.error('WebSocket error:', error);
};
return () => {
if (wsRef.current) {
wsRef.current.close();
}
};
}, []);
const sendMessage = () => {
if (input.trim() && wsRef.current) {
wsRef.current.send(JSON.stringify({
type: 'message',
text: input,
timestamp: new Date().toISOString(),
}));
setInput('');
}
};
return (
<div className="flex flex-col h-screen">
<div className="flex-1 overflow-y-auto p-4 space-y-4">
{messages.map((msg, idx) => (
<div key={idx} className="p-3 bg-gray-100 rounded">
<p>{msg.text}</p>
<span className="text-xs text-gray-500">{msg.timestamp}</span>
</div>
))}
</div>
<div className="p-4 border-t flex gap-2">
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && sendMessage()}
placeholder="Сообщение..."
className="flex-1 px-4 py-2 border rounded"
/>
<button
onClick={sendMessage}
className="px-6 py-2 bg-blue-600 text-white rounded"
>
Отправить
</button>
</div>
</div>
);
}
3. Графический редактор (Canvas API)
export function SimpleDrawingApp() {
const canvasRef = useRef(null);
const [isDrawing, setIsDrawing] = useState(false);
const startDrawing = (e) => {
const canvas = canvasRef.current;
const ctx = canvas.getContext('2d');
const rect = canvas.getBoundingClientRect();
ctx.beginPath();
ctx.moveTo(
e.clientX - rect.left,
e.clientY - rect.top
);
setIsDrawing(true);
};
const draw = (e) => {
if (!isDrawing) return;
const canvas = canvasRef.current;
const ctx = canvas.getContext('2d');
const rect = canvas.getBoundingClientRect();
ctx.lineTo(
e.clientX - rect.left,
e.clientY - rect.top
);
ctx.stroke();
};
const stopDrawing = () => {
setIsDrawing(false);
};
return (
<canvas
ref={canvasRef}
width={800}
height={600}
onMouseDown={startDrawing}
onMouseMove={draw}
onMouseUp={stopDrawing}
onMouseLeave={stopDrawing}
className="border-2 border-gray-300 cursor-crosshair"
/>
);
}
4. Интерактивные формы с валидацией
interface FormData {
name: string;
email: string;
password: string;
confirmPassword: string;
}
interface FormErrors {
name?: string;
email?: string;
password?: string;
confirmPassword?: string;
}
export function RegistrationForm() {
const [formData, setFormData] = useState<FormData>({
name: '',
email: '',
password: '',
confirmPassword: '',
});
const [errors, setErrors] = useState<FormErrors>({});
const [touched, setTouched] = useState<Record<string, boolean>>({});
const validateField = (name: string, value: string) => {
const newErrors: FormErrors = {};
switch (name) {
case 'name':
if (!value.trim()) newErrors.name = 'Имя обязательно';
if (value.length < 3) newErrors.name = 'Минимум 3 символа';
break;
case 'email':
if (!value.includes('@')) newErrors.email = 'Неверный email';
break;
case 'password':
if (value.length < 8) newErrors.password = 'Минимум 8 символов';
if (!/[A-Z]/.test(value)) newErrors.password = 'Нужна заглавная буква';
if (!/[0-9]/.test(value)) newErrors.password = 'Нужна цифра';
break;
case 'confirmPassword':
if (value !== formData.password) {
newErrors.confirmPassword = 'Пароли не совпадают';
}
break;
}
return newErrors;
};
const handleChange = (e) => {
const { name, value } = e.target;
setFormData(prev => ({ ...prev, [name]: value }));
if (touched[name]) {
const fieldErrors = validateField(name, value);
setErrors(prev => ({ ...prev, ...fieldErrors }));
}
};
const handleBlur = (e) => {
const { name, value } = e.target;
setTouched(prev => ({ ...prev, [name]: true }));
const fieldErrors = validateField(name, value);
setErrors(prev => ({ ...prev, ...fieldErrors }));
};
const handleSubmit = (e) => {
e.preventDefault();
const allErrors = {};
Object.keys(formData).forEach(key => {
Object.assign(allErrors, validateField(key, formData[key]));
});
if (Object.keys(allErrors).length === 0) {
console.log('Форма отправлена:', formData);
} else {
setErrors(allErrors);
}
};
return (
<form onSubmit={handleSubmit} className="space-y-4 max-w-md">
{Object.entries(formData).map(([key, value]) => (
<div key={key}>
<label className="block text-sm font-medium mb-1">
{key.charAt(0).toUpperCase() + key.slice(1)}
</label>
<input
type={key.includes('password') ? 'password' : 'email' if key === 'email' : 'text'}
name={key}
value={value}
onChange={handleChange}
onBlur={handleBlur}
className={`w-full px-4 py-2 border rounded ${
errors[key] ? 'border-red-500' : 'border-gray-300'
}`}
/>
{errors[key] && (
<p className="text-red-500 text-sm mt-1">{errors[key]}</p>
)}
</div>
))}
<button type="submit" className="w-full bg-blue-600 text-white py-2 rounded">
Зарегистрироваться
</button>
</form>
);
}
5. Приложение с состоянием и сложной логикой
// Приложение для фильтрации и сортировки товаров
interface Product {
id: number;
name: string;
price: number;
category: string;
rating: number;
}
interface Filters {
category: string | null;
minPrice: number;
maxPrice: number;
sortBy: 'name' | 'price' | 'rating';
sortOrder: 'asc' | 'desc';
}
export function ProductCatalog() {
const [products] = useState<Product[]>([
{ id: 1, name: 'Ноутбук', price: 50000, category: 'electronics', rating: 4.5 },
{ id: 2, name: 'Мышка', price: 500, category: 'electronics', rating: 4.2 },
{ id: 3, name: 'Клавиатура', price: 2000, category: 'electronics', rating: 4.8 },
]);
const [filters, setFilters] = useState<Filters>({
category: null,
minPrice: 0,
maxPrice: 100000,
sortBy: 'name',
sortOrder: 'asc',
});
const filteredProducts = useMemo(() => {
let result = [...products];
// Фильтр по категории
if (filters.category) {
result = result.filter(p => p.category === filters.category);
}
// Фильтр по цене
result = result.filter(
p => p.price >= filters.minPrice && p.price <= filters.maxPrice
);
// Сортировка
result.sort((a, b) => {
let aVal = a[filters.sortBy];
let bVal = b[filters.sortBy];
if (filters.sortOrder === 'asc') {
return aVal > bVal ? 1 : -1;
} else {
return aVal < bVal ? 1 : -1;
}
});
return result;
}, [products, filters]);
return (
<div className="p-4">
<h1 className="text-2xl font-bold mb-4">Каталог товаров</h1>
<div className="grid grid-cols-4 gap-4 mb-6">
<select
value={filters.category || ''}
onChange={(e) => setFilters(prev => ({
...prev,
category: e.target.value || null
}))}
className="px-4 py-2 border rounded"
>
<option value="">Все категории</option>
<option value="electronics">Электроника</option>
<option value="books">Книги</option>
</select>
<input
type="number"
min="0"
value={filters.minPrice}
onChange={(e) => setFilters(prev => ({
...prev,
minPrice: parseInt(e.target.value)
}))}
placeholder="От"
className="px-4 py-2 border rounded"
/>
<input
type="number"
min="0"
value={filters.maxPrice}
onChange={(e) => setFilters(prev => ({
...prev,
maxPrice: parseInt(e.target.value)
}))}
placeholder="До"
className="px-4 py-2 border rounded"
/>
<select
value={filters.sortBy}
onChange={(e) => setFilters(prev => ({
...prev,
sortBy: e.target.value as any
}))}
className="px-4 py-2 border rounded"
>
<option value="name">По названию</option>
<option value="price">По цене</option>
<option value="rating">По рейтингу</option>
</select>
</div>
<div className="grid grid-cols-3 gap-4">
{filteredProducts.map(product => (
<div key={product.id} className="p-4 border rounded-lg">
<h3 className="font-bold mb-2">{product.name}</h3>
<p className="text-lg font-semibold mb-1">{product.price} руб.</p>
<p className="text-yellow-500">Рейтинг: {product.rating}</p>
</div>
))}
</div>
</div>
);
}
Кейсы использования
- Админ-панели — управление данными, таблицы, графики
- E-commerce — каталог, корзина, оформление заказов
- Социальные сети — ленты, профили, уведомления
- Аналитика — dashboards с графиками и метриками
- Real-time приложения — чат, совместное редактирование
- SaaS системы — управление проектами, CRM
Технологии, которые я использую
- React 19 с hooks и контекстом
- Redux/Context API для управления состоянием
- TypeScript для типизации
- WebSocket для real-time
- Canvas API для графики
- Local Storage для кэширования
- Fetch API и axios для запросов
Мой опыт включает создание приложений, которые хорошо реагируют на взаимодействие пользователя, обрабатывают сложную логику и масштабируются.