← Назад к вопросам

Делал ли приложения на 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 для запросов

Мой опыт включает создание приложений, которые хорошо реагируют на взаимодействие пользователя, обрабатывают сложную логику и масштабируются.