Lin / REACT_DEVELOPMENT_GUIDE.md
Zelyanoth's picture
fff
25f22bf

React Development Guide for Lin Project

Overview

This guide documents the React development patterns, best practices, and conventions used in the Lin project. It serves as a reference for current and future developers working on the frontend.

Project Structure

The frontend follows a component-based architecture with clear separation of concerns:

frontend/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ components/        # Reusable UI components
β”‚   β”‚   β”œβ”€β”€ Header/        # Header component
β”‚   β”‚   β”œβ”€β”€ Sidebar/       # Sidebar navigation
β”‚   β”‚   └── ...            # Other reusable components
β”‚   β”œβ”€β”€ pages/             # Page-level components
β”‚   β”œβ”€β”€ services/          # API service layer
β”‚   β”œβ”€β”€ store/             # Redux store configuration
β”‚   β”œβ”€β”€ App.jsx            # Root component
β”‚   └── index.jsx          # Entry point
β”œβ”€β”€ public/                # Static assets
└── package.json           # Dependencies and scripts

Core Technologies

  • React 18+ with Hooks
  • React Router v6 for routing
  • Redux Toolkit for state management
  • Axios for HTTP requests
  • Tailwind CSS for styling
  • Material Icons for icons

Component Development Patterns

Functional Components with Hooks

All components are implemented as functional components using React hooks:

import React, { useState, useEffect } from 'react';

const MyComponent = ({ prop1, prop2 }) => {
  const [state, setState] = useState(initialValue);
  
  useEffect(() => {
    // Side effects
  }, [dependencies]);
  
  return (
    <div className="component-class">
      {/* JSX */}
    </div>
  );
};

export default MyComponent;

Component Structure

  1. Imports - All necessary imports at the top
  2. Component Definition - Functional component with destructured props
  3. State Management - useState, useEffect, and custom hooks
  4. Helper Functions - Small utility functions within the component
  5. JSX Return - Clean, semantic HTML with Tailwind classes
  6. Export - Default export of the component

State Management

Local Component State

Use useState for local component state:

const [isOpen, setIsOpen] = useState(false);
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);

Global State (Redux)

Use Redux Toolkit for global state management. Structure slices by feature:

// store/reducers/featureSlice.js
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';

export const fetchFeatureData = createAsyncThunk(
  'feature/fetchData',
  async (params) => {
    const response = await api.getFeatureData(params);
    return response.data;
  }
);

const featureSlice = createSlice({
  name: 'feature',
  initialState: {
    items: [],
    loading: false,
    error: null
  },
  reducers: {
    clearError: (state) => {
      state.error = null;
    }
  },
  extraReducers: (builder) => {
    builder
      .addCase(fetchFeatureData.pending, (state) => {
        state.loading = true;
      })
      .addCase(fetchFeatureData.fulfilled, (state, action) => {
        state.loading = false;
        state.items = action.payload;
      })
      .addCase(fetchFeatureData.rejected, (state, action) => {
        state.loading = false;
        state.error = action.error.message;
      });
  }
});

export const { clearError } = featureSlice.actions;
export default featureSlice.reducer;

Props and Prop Types

Destructure props in the component signature and provide default values when appropriate:

const MyComponent = ({ 
  title, 
  items = [], 
  isLoading = false, 
  onAction = () => {} 
}) => {
  // Component implementation
};

Event Handling

Use inline arrow functions or separate handler functions:

// Inline
<button onClick={() => handleClick(item.id)}>Click me</button>

// Separate function
const handleDelete = (id) => {
  dispatch(deleteItem(id));
};

<button onClick={() => handleDelete(item.id)}>Delete</button>

Styling with Tailwind CSS

The project uses Tailwind CSS for styling. Follow these conventions:

Class Organization

  1. Layout classes (flex, grid, etc.) first
  2. Positioning (relative, absolute, etc.)
  3. Sizing (w-, h-, etc.)
  4. Spacing (m-, p-, etc.)
  5. Typography (text-, font-, etc.)
  6. Visual (bg-, border-, shadow-, etc.)
  7. Interactive states (hover:, focus:, etc.)
<div className="flex items-center justify-between w-full p-4 bg-white rounded-lg shadow hover:shadow-md focus:outline-none focus:ring-2 focus:ring-primary-500">
  {/* Content */}
</div>

Responsive Design

Use Tailwind's responsive prefixes (sm:, md:, lg:, xl:) for responsive styles:

<div className="flex flex-col lg:flex-row items-center p-4 sm:p-6">
  <div className="w-full lg:w-1/2 mb-4 lg:mb-0 lg:mr-6">
    {/* Content */}
  </div>
  <div className="w-full lg:w-1/2">
    {/* Content */}
  </div>
</div>

Custom Classes

For complex components, use component-specific classes in conjunction with Tailwind:

<NavLink 
  to={item.path}
  className={({ isActive }) => `
    nav-link group relative flex items-center px-3 py-2.5 text-sm font-medium rounded-lg
    transition-all duration-200 ease-in-out
    ${isActive 
      ? 'bg-gradient-to-r from-primary-600 to-primary-700 text-white' 
      : 'text-secondary-700 hover:bg-accent-100'
    }
  `}
>

Routing

Use React Router v6 for navigation:

import { Routes, Route, useNavigate, useLocation } from 'react-router-dom';

// In App.jsx
<Routes>
  <Route path="/dashboard" element={<Dashboard />} />
  <Route path="/sources" element={<Sources />} />
  <Route path="/posts" element={<Posts />} />
  <Route path="/schedule" element={<Schedule />} />
</Routes>

// In components
const navigate = useNavigate();
const location = useLocation();

// Navigation
navigate('/dashboard');

// Check current route
if (location.pathname === '/dashboard') {
  // Do something
}

API Integration

Service Layer

Create service functions for API calls:

// services/api.js
import axios from 'axios';

const API_BASE_URL = process.env.REACT_APP_API_URL || 'http://localhost:5000';

const api = axios.create({
  baseURL: API_BASE_URL,
  timeout: 10000,
});

// Request interceptor
api.interceptors.request.use((config) => {
  const token = localStorage.getItem('token');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

// Response interceptor
api.interceptors.response.use(
  (response) => response,
  (error) => {
    if (error.response?.status === 401) {
      // Handle unauthorized access
      localStorage.removeItem('token');
      window.location.href = '/login';
    }
    return Promise.reject(error);
  }
);

export default api;

// services/featureService.js
import api from './api';

export const getFeatures = async () => {
  const response = await api.get('/api/features');
  return response.data;
};

export const createFeature = async (data) => {
  const response = await api.post('/api/features', data);
  return response.data;
};

Async Operations with Redux Thunks

Use createAsyncThunk for asynchronous operations:

// In slice
export const fetchData = createAsyncThunk(
  'feature/fetchData',
  async (_, { rejectWithValue }) => {
    try {
      const data = await featureService.getFeatures();
      return data;
    } catch (error) {
      return rejectWithValue(error.response.data);
    }
  }
);

// In component
const dispatch = useDispatch();
const { items, loading, error } = useSelector(state => state.feature);

useEffect(() => {
  dispatch(fetchData());
}, [dispatch]);

Accessibility (a11y)

Implement proper accessibility features:

  1. Semantic HTML - Use appropriate HTML elements
  2. ARIA attributes - When needed for dynamic content
  3. Keyboard navigation - Ensure all interactive elements are keyboard accessible
  4. Focus management - Proper focus handling for modals, dropdowns, etc.
  5. Screen reader support - Use aria-label, aria-describedby, etc.
<button 
  aria-label="Close dialog"
  aria-expanded={isOpen}
  onClick={handleClose}
>
  βœ•
</button>

<nav aria-label="Main navigation">
  <ul role="menubar">
    <li role="none">
      <a 
        href="/dashboard" 
        role="menuitem"
        aria-current={currentPage === 'dashboard' ? 'page' : undefined}
      >
        Dashboard
      </a>
    </li>
  </ul>
</nav>

Performance Optimization

Memoization

Use React.memo for components that render frequently:

const MyComponent = React.memo(({ data, onUpdate }) => {
  // Component implementation
});

export default MyComponent;

useCallback and useMemo

Optimize expensive calculations and callback functions:

const expensiveValue = useMemo(() => {
  return computeExpensiveValue(data);
}, [data]);

const handleClick = useCallback((id) => {
  dispatch(action(id));
}, [dispatch]);

Lazy Loading

Use React.lazy for code splitting:

import { lazy, Suspense } from 'react';

const LazyComponent = lazy(() => import('./components/MyComponent'));

<Suspense fallback={<div>Loading...</div>}>
  <LazyComponent />
</Suspense>

Error Handling

Error Boundaries

Implement error boundaries for catching JavaScript errors:

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  componentDidCatch(error, errorInfo) {
    console.error('Error caught by boundary:', error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children;
  }
}

// Usage
<ErrorBoundary>
  <MyComponent />
</ErrorBoundary>

API Error Handling

Handle API errors gracefully:

const [error, setError] = useState(null);

const handleSubmit = async (data) => {
  try {
    setError(null);
    const result = await api.createItem(data);
    // Handle success
  } catch (err) {
    setError(err.response?.data?.message || 'An error occurred');
  }
};

{error && (
  <div className="text-red-500 text-sm mt-2">
    {error}
  </div>
)}

Testing

Unit Testing

Use Jest and React Testing Library for unit tests:

import { render, screen, fireEvent } from '@testing-library/react';
import MyComponent from './MyComponent';

test('renders component with title', () => {
  render(<MyComponent title="Test Title" />);
  expect(screen.getByText('Test Title')).toBeInTheDocument();
});

test('calls onClick when button is clicked', () => {
  const handleClick = jest.fn();
  render(<MyComponent onClick={handleClick} />);
  fireEvent.click(screen.getByRole('button'));
  expect(handleClick).toHaveBeenCalledTimes(1);
});

Redux Testing

Test Redux slices and async thunks:

import featureReducer, { fetchData } from './featureSlice';

test('handles fulfilled fetch data', () => {
  const initialState = { items: [], loading: false, error: null };
  const data = [{ id: 1, name: 'Test' }];
  const action = { type: fetchData.fulfilled, payload: data };
  const state = featureReducer(initialState, action);
  expect(state.items).toEqual(data);
  expect(state.loading).toBe(false);
});

Mobile Responsiveness

Touch Optimization

Add touch-specific classes and handlers:

<button 
  className="touch-manipulation active:scale-95"
  onTouchStart={handleTouchStart}
  onTouchEnd={handleTouchEnd}
>
  Click me
</button>

Responsive Breakpoints

Use Tailwind's responsive utilities for different screen sizes:

  • Mobile: Default styles (0-767px)
  • Tablet: sm: (768px+) and md: (1024px+)
  • Desktop: lg: (1280px+) and xl: (1536px+)

Mobile-First Approach

Start with mobile styles and enhance for larger screens:

<div className="flex flex-col lg:flex-row">
  <div className="w-full lg:w-1/2">
    {/* Content that stacks on mobile, side-by-side on desktop */}
  </div>
</div>

Best Practices

Component Design

  1. Single Responsibility - Each component should have one clear purpose
  2. Reusability - Design components to be reusable across the application
  3. Composition - Build complex UIs by composing simpler components
  4. Controlled Components - Prefer controlled components for form elements
  5. Props Drilling - Use context or Redux to avoid excessive prop drilling

Code Organization

  1. Consistent Naming - Use consistent naming conventions (PascalCase for components)
  2. Logical Grouping - Group related files in directories
  3. Export Strategy - Use default exports for components, named exports for utilities
  4. Import Organization - Group imports logically (external, internal, styles)

Performance

  1. Bundle Size - Monitor bundle size and optimize when necessary
  2. Rendering - Use React.memo, useMemo, and useCallback appropriately
  3. API Calls - Implement caching and pagination where appropriate
  4. Images - Optimize images and use lazy loading

Security

  1. XSS Prevention - React automatically escapes content, but be careful with dangerouslySetInnerHTML
  2. Token Storage - Store JWT tokens securely (HttpOnly cookies or secure localStorage)
  3. Input Validation - Validate and sanitize user inputs
  4. CORS - Ensure proper CORS configuration on the backend

This guide should help maintain consistency and quality across the React frontend implementation in the Lin project.