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:
```jsx
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:
```jsx
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:
```jsx
// 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:
```jsx
const MyComponent = ({
title,
items = [],
isLoading = false,
onAction = () => {}
}) => {
// Component implementation
};
```
### Event Handling
Use inline arrow functions or separate handler functions:
```jsx
// 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.)
```jsx
<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:
```jsx
<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:
```jsx
<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:
```jsx
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:
```jsx
// 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:
```jsx
// 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.
```jsx
<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:
```jsx
const MyComponent = React.memo(({ data, onUpdate }) => {
// Component implementation
});
export default MyComponent;
```
### useCallback and useMemo
Optimize expensive calculations and callback functions:
```jsx
const expensiveValue = useMemo(() => {
return computeExpensiveValue(data);
}, [data]);
const handleClick = useCallback((id) => {
dispatch(action(id));
}, [dispatch]);
```
### Lazy Loading
Use React.lazy for code splitting:
```jsx
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:
```jsx
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:
```jsx
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:
```jsx
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:
```jsx
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:
```jsx
<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:
```jsx
<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.