Темный режим

7.2. Survey - Архитектура приложения

Общая архитектура

Проект Survey использует типичную архитектуру React-приложения с клиентской маршрутизацией и интеграцией с Firebase для хранения данных и аутентификации. Проект следует принципам компонентного подхода и разделения ответственности.

Основные архитектурные слои приложения:

  • Презентационный слой (UI) - React-компоненты, отвечающие за отображение интерфейса
  • Слой бизнес-логики - функции и хуки для обработки данных и управления состоянием
  • Слой данных - взаимодействие с API и базой данных Firebase

Компонентная структура

Приложение построено по принципу компонентной архитектуры, где каждый компонент отвечает за определенную функциональность. Компоненты организованы иерархически и взаимодействуют друг с другом через props и контекст.

Ключевые компоненты

  • App - корневой компонент, определяющий структуру маршрутизации
  • SurveyCard - основной контейнер для отображения опроса
  • QuestionInterface - компонент для отображения вопроса и интерфейса ответа
  • NavigationControls - компонент для навигации по вопросам
  • SurveyCompleted - компонент, отображаемый при завершении опроса

Схема основных компонентов:

App
└── BrowserRouter
    └── Routes
        └── Route (/survey)
            ├── SurveyCard
            │   ├── Header
            │   ├── QuestionInterface
            │   │   └── [AnswerInterface]
            │   └── NavigationControls
            ├── SurveyCompleted
            ├── SurveyEnded
            ├── PreparationSteps
            ├── DifficultyModal
            └── CompletionScreen
                

Маршрутизация

Для маршрутизации в приложении используется библиотека React Router. Основная структура маршрутизации определена в компоненте App.

Пример маршрутизации

<BrowserRouter>
  <Routes>
    <Route path="/" element={<Navigate to="/survey" />} />
    <Route 
      path="/survey" 
      element={
        // Основной контент приложения
      } 
    />
    <Route path="*" element={<Navigate to="/survey" />} />
  </Routes>
</BrowserRouter>
                    

Управление состоянием

В проекте для управления состоянием используется встроенный в React хук useState. Основные состояния приложения определены в компоненте App и передаются в дочерние компоненты через props.

Ключевые состояния приложения:

  • surveyData - данные опроса, полученные с сервера
  • step - текущий шаг прохождения опроса
  • currentQuestionIndex - индекс текущего вопроса
  • answers - объект с ответами пользователя
Пример управления состоянием

// Состояния в компоненте App
const [surveyData, setSurveyData] = useState<any>(null);
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<boolean>(false);
const [step, setStep] = useState<number>(0);
const [currentQuestionIndex, setCurrentQuestionIndex] = useState<number>(0);
const [answers, setAnswers] = useState<Record<string, any>>({});

// Обработчик изменения ответа
const handleAnswerChange = (questionId: string, value: any) => {
  setAnswers(prev => ({
    ...prev,
    [questionId]: value
  }));
                    

Работа с данными

Приложение взаимодействует с данными через API-функции и Firebase. Данные опросов загружаются асинхронно и сохраняются в состоянии приложения.

Загрузка данных опроса

Пример загрузки данных

// Функция загрузки данных опроса в App.tsx
useEffect(() => {
  const fetchSurveyData = async () => {
    try {
      const urlParams = new URLSearchParams(window.location.search);
      const surveyId = urlParams.get('surveyId');
      const userId = urlParams.get('userId');
      
      if (!surveyId || !userId) {
        setError(true);
        setLoading(false);
        return;
      }
      
      const data = await getSurveyData(surveyId, userId);
      
      if (!data || data.length === 0 || !data[0].questions) {
        setError(true);
        setLoading(false);
        return;
      }
      
      setSurveyData(data);
      // Загрузка сохраненного состояния опроса, если есть
      loadSavedState(surveyId, userId);
    } catch (error) {
      console.error('Error fetching survey data:', error);
      setError(true);
    } finally {
      setLoading(false);
    }
  };
  
  fetchSurveyData();
}, []);
                    

Сохранение ответов

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

Пример сохранения ответов

// Функция сохранения состояния опроса в surveyPersistence.ts
export const saveSurveyState = (
  surveyId: string,
  userId: string,
  answers: Record<string, any>,
  currentQuestionIndex: number
) => {
  try {
    const stateKey = `surveyState_${surveyId}_${userId}`;
    const state = {
      answers,
      currentQuestionIndex,
      timestamp: new Date().toISOString()
    };
    
    localStorage.setItem(stateKey, JSON.stringify(state));
    return true;
  } catch (error) {
    console.error('Error saving survey state:', error);
    return false;
  }
                    

Обработка ошибок

В приложении реализована обработка ошибок на различных уровнях:

  • Состояние error для отображения ошибок загрузки данных
  • Перехват ошибок в асинхронных операциях с использованием try/catch
  • Проверка полученных данных на валидность
Пример обработки ошибок

// Пример обработки ошибок при загрузке данных
try {
  const data = await getSurveyData(surveyId, userId);
  
  if (!data || data.length === 0 || !data[0].questions) {
    setError(true);
    setLoading(false);
    return;
  }
  
  setSurveyData(data);
} catch (error) {
  console.error('Error fetching survey data:', error);
  setError(true);
} finally {
  setLoading(false);
}

// Отображение ошибки в UI
{error && (
  <div className="error-container">
    <h2>Произошла ошибка при загрузке опроса</h2>
    <p>Пожалуйста, проверьте параметры URL или попробуйте позже.</p>
  </div>
)}