Welcome! This guide is designed to get you up to speed quickly, even if you're new to React, TypeScript, or modern frontend development. We'll cover everything from setup to debugging to testing.
- Project Overview
- Prerequisites & Setup
- Project Architecture
- Development Workflow
- Key Technologies Explained
- Browser Developer Tools Mastery
- Testing Guide
- Component Development
- API Integration
- Common Patterns & Best Practices
- Troubleshooting
- Resources for Learning
Deepiri is a gamified productivity platform that transforms work into an engaging experience. The frontend is built with modern React, TypeScript, and Vite, providing a fast, interactive user experience.
- Gamification System: Tracks momentum (XP), streaks, boosts, objectives, odysseys, and seasons
- Real-time Collaboration: Socket.IO for live updates and notifications
- Task Management: AI-powered task generation and tracking
- Analytics Dashboard: Visual insights into productivity metrics
- Social Features: Friends, leaderboards, and team collaboration
- React 18 - UI library
- TypeScript - Type safety
- Vite - Build tool (faster than Create React App)
- React Router - Navigation
- React Query - Server state management
- Axios - HTTP client
- Socket.IO Client - Real-time communication
- Framer Motion - Animations
- Vitest - Testing framework
Minimum:
- Basic HTML, CSS, JavaScript
- Understanding of functions, objects, arrays
Helpful but Not Required:
- React basics (we'll cover it)
- TypeScript basics (we'll cover it)
- Node.js/npm basics
Download and install Node.js 18+ (LTS version recommended).
Verify installation:
node --version # Should show v18.x.x or higher
npm --version # Should show 9.x.x or higher# Navigate to the frontend directory
cd deepiri/deepiri-web-frontend
# Install dependencies
npm install
# If you encounter peer dependency issues:
npm install --legacy-peer-depsCopy the example environment file:
cp env.example.frontend .envEdit .env with your configuration:
VITE_API_URL=http://localhost:5000/api
VITE_CYREX_URL=http://localhost:8000
VITE_FIREBASE_API_KEY=your-key-here
VITE_FIREBASE_AUTH_DOMAIN=your-domain-here
# ... other Firebase config# Standard development mode
npm run dev
# Fast mode (skips some checks)
npm run dev:fast
# Debug mode (verbose logging)
npm run dev:debugThe app will be available at http://localhost:5173
deepiri-web-frontend/
βββ src/
β βββ api/ # API client functions
β β βββ axiosInstance.ts # Axios configuration
β β βββ authApi.ts # Authentication endpoints
β β βββ gamificationApi.ts # Gamification endpoints
β β βββ ...
β β
β βββ components/ # Reusable UI components
β β βββ Button.tsx
β β βββ Navbar.tsx
β β βββ gamification/ # Gamification-specific components
β β βββ ...
β β
β βββ contexts/ # React Context providers
β β βββ AuthContext.tsx # Authentication state
β β βββ SocketContext.tsx # WebSocket connections
β β βββ ...
β β
β βββ hooks/ # Custom React hooks
β β βββ useMultiplayer.ts
β β βββ ...
β β
β βββ pages/ # Page components (routes)
β β βββ Home.tsx
β β βββ Dashboard.tsx
β β βββ Login.tsx
β β βββ ...
β β
β βββ services/ # Business logic services
β β βββ multiplayerService.ts
β β βββ ...
β β
β βββ styles/ # Global CSS files
β β βββ index.css
β β βββ App.css
β β βββ ...
β β
β βββ tests/ # Test files
β β βββ components.test.jsx
β β
β βββ types/ # TypeScript type definitions
β β βββ common.ts
β β
β βββ utils/ # Utility functions
β β βββ logger.ts
β β βββ testHelpers.tsx
β β
β βββ App.tsx # Main app component
β βββ main.tsx # Application entry point
β
βββ public/ # Static assets
βββ dist/ # Production build output
βββ vite.config.ts # Vite configuration
βββ tsconfig.json # TypeScript configuration
βββ package.json # Dependencies and scripts
- Pages (
src/pages/): Full page views, usually correspond to routes - Components (
src/components/): Reusable UI pieces used across pages
React Context provides global state without prop drilling:
AuthContext: User authentication stateSocketContext: Real-time WebSocket connectionsAdventureContext: Adventure/gamification state
All backend communication goes through:
axiosInstance.ts: Configured Axios client with interceptors- Individual API files: Organized by feature (auth, gamification, etc.)
-
Start the dev server
npm run dev
-
Make changes - Vite's HMR (Hot Module Replacement) will automatically refresh
-
Check the browser console - Errors and warnings appear here
-
Use React DevTools - Install the React DevTools browser extension
-
Run tests (we'll cover this later)
npm test
- One component per file - Each component gets its own
.tsxfile - Co-locate related code - Keep styles, tests, and components together when possible
- Use TypeScript - Type everything for better IDE support and fewer bugs
- Follow naming conventions:
- Components:
PascalCase.tsx(e.g.,UserProfile.tsx) - Utilities:
camelCase.ts(e.g.,formatDate.ts) - Constants:
UPPER_SNAKE_CASE(e.g.,API_BASE_URL)
- Components:
# Create a new branch for your feature
git checkout -b feature/your-feature-name
# Make your changes, then:
git add .
git commit -m "feat: add user profile page"
# Push and create a pull request
git push origin feature/your-feature-nameReact is a library for building user interfaces using components - reusable pieces of UI.
// Simple component
function Welcome({ name }: { name: string }) {
return <h1>Hello, {name}!</h1>;
}
// Usage
<Welcome name="Alice" />useState - Manage component state:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}useEffect - Run code after render (API calls, subscriptions):
import { useEffect, useState } from 'react';
function UserProfile({ userId }: { userId: string }) {
const [user, setUser] = useState(null);
useEffect(() => {
// Fetch user data when component mounts or userId changes
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => setUser(data));
}, [userId]); // Dependency array - re-run when userId changes
if (!user) return <div>Loading...</div>;
return <div>{user.name}</div>;
}TypeScript adds types to JavaScript to catch errors early.
// JavaScript (no types)
function greet(name) {
return `Hello, ${name}`;
}
// TypeScript (with types)
function greet(name: string): string {
return `Hello, ${name}`;
}
// Interface (defining object shapes)
interface User {
id: string;
name: string;
email: string;
age?: number; // Optional property
}
function displayUser(user: User) {
console.log(user.name);
}React Query handles fetching, caching, and synchronizing server state.
import { useQuery } from 'react-query';
import { gamificationApi } from '../api/gamificationApi';
function MomentumDisplay() {
// React Query automatically handles loading, error, and data states
const { data, isLoading, error } = useQuery(
'momentum', // Cache key
() => gamificationApi.getMomentum() // Fetch function
);
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error loading momentum</div>;
return <div>Momentum: {data.momentum}</div>;
}import { Routes, Route, Link } from 'react-router-dom';
function App() {
return (
<div>
<nav>
<Link to="/">Home</Link>
<Link to="/dashboard">Dashboard</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
</div>
);
}The browser DevTools are your best friend for debugging. Here's how to use them effectively.
- Chrome/Edge:
F12orCtrl+Shift+I(Windows) /Cmd+Option+I(Mac) - Firefox:
F12orCtrl+Shift+I(Windows) /Cmd+Option+I(Mac) - Safari: Enable in Preferences β Advanced β "Show Develop menu"
Viewing Logs:
// In your code:
console.log('User clicked button', { userId: 123 });
console.warn('This is a warning');
console.error('This is an error');
console.table([{ name: 'Alice', age: 30 }, { name: 'Bob', age: 25 }]);Interactive Console:
- Type JavaScript directly to test code
- Access global variables:
window,document,localStorage - Inspect React components: Install React DevTools, then
$rin console
Filtering:
- Click the filter icon to show only errors, warnings, or logs
- Use the search box to find specific messages
Inspecting Elements:
- Click the element selector (top-left icon) or press
Ctrl+Shift+C - Hover over elements on the page
- Click to select and inspect
What You Can Do:
- Edit HTML/CSS live: Right-click β "Edit as HTML" or modify styles in the right panel
- See computed styles: View final CSS after all rules applied
- Check accessibility: Look for ARIA labels, semantic HTML
- Find React component: With React DevTools, see which component rendered this element
Finding Elements:
// In console:
document.querySelector('.my-class') // First match
document.querySelectorAll('.my-class') // All matches
$('.my-class') // jQuery-style (if available)What to Look For:
- Request/Response: See what data is sent and received
- Status codes: 200 = success, 404 = not found, 500 = server error
- Timing: How long requests take
- Headers: Authentication tokens, content types
Filtering:
- XHR/Fetch: API calls
- JS: JavaScript files
- CSS: Stylesheets
- Img: Images
Debugging Failed Requests:
- Click the failed request (red status)
- Check "Headers" tab for request details
- Check "Response" tab for error message
- Check "Preview" tab for formatted JSON
Example Debugging:
// In console, intercept fetch calls:
const originalFetch = window.fetch;
window.fetch = function(...args) {
console.log('Fetch called:', args);
return originalFetch.apply(this, args)
.then(response => {
console.log('Response:', response);
return response;
});
};Setting Breakpoints:
- Open Sources tab
- Navigate to your file (e.g.,
src/components/Button.tsx) - Click line number to set breakpoint
- Refresh page - execution pauses at breakpoint
Debugging Controls:
- Resume (F8): Continue execution
- Step Over (F10): Execute current line, don't enter functions
- Step Into (F11): Enter function calls
- Step Out (Shift+F11): Exit current function
Watch Expressions:
- Add variables to watch their values change
- Right-click variable β "Add to watch"
Call Stack:
- See the chain of function calls that led to current point
Local Storage:
- View stored data (tokens, user info)
- Right-click to edit/delete
- In console:
localStorage.getItem('token')
Session Storage:
- Similar to localStorage but cleared on tab close
Cookies:
- View authentication cookies
Service Workers:
- Check if service worker is registered
- Unregister for testing
Install:
Features:
- Components Tab: Tree view of React component hierarchy
- Profiler Tab: Performance analysis
- Inspect Component: Click element on page, see component in tree
- Props/State: View component props and state
- Hooks: Inspect hook values
Usage:
- Open DevTools β "Components" tab
- Click element on page or select component in tree
- View props, state, hooks on right panel
- Edit props/state to test different scenarios
Recording:
- Click "Record" (circle icon)
- Perform actions on page
- Stop recording
- Analyze timeline
What to Look For:
- Long tasks (red bars) - JavaScript blocking UI
- Layout shifts - Elements moving unexpectedly
- Memory leaks - Memory usage growing over time
// Add console.log to see if component is called
function MyComponent() {
console.log('MyComponent rendered');
return <div>Hello</div>;
}
// Check React DevTools - is component in tree?
// Check Elements tab - is HTML present?
// Check Console - any errors?// Add logging to API call
const response = await fetch('/api/users');
console.log('Response status:', response.status);
console.log('Response headers:', response.headers);
const data = await response.json();
console.log('Response data:', data);In Network Tab:
- Find the request
- Check status code
- Check request payload
- Check response body
// Add useEffect to watch state changes
useEffect(() => {
console.log('State changed:', myState);
}, [myState]);
// Check React DevTools - is state actually changing?
// Check if you're mutating state directly (bad!)
// setState([...oldArray, newItem]); // β
Correct
// oldArray.push(newItem); // β Wrong// In Elements tab:
// 1. Inspect element
// 2. Check "Computed" styles
// 3. See which CSS rules are applied
// 4. Toggle styles in right panel to test
// In console:
const element = document.querySelector('.my-class');
console.log(getComputedStyle(element)); // All computed styles-
Use
debugger;statement:function handleClick() { debugger; // Execution pauses here if DevTools is open // Your code }
-
Console.table() for arrays/objects:
console.table([{ name: 'Alice', age: 30 }, { name: 'Bob', age: 25 }]);
-
Time operations:
console.time('myOperation'); // ... your code ... console.timeEnd('myOperation'); // Logs elapsed time
-
Group related logs:
console.group('User Actions'); console.log('Clicked button'); console.log('Submitted form'); console.groupEnd();
We use Vitest (similar to Jest) for testing. It's fast, works with Vite, and has a Jest-compatible API.
- Catch bugs early - Before users see them
- Document behavior - Tests show how code should work
- Refactor safely - Tests ensure changes don't break existing features
- Confidence - Deploy knowing your code works
- Unit Tests: Test individual functions/components in isolation
- Integration Tests: Test how multiple pieces work together
- E2E Tests: Test full user flows (we use Playwright/Cypress for this)
# Run all tests in watch mode (reruns on file changes)
npm test
# Run tests once
npm test -- --run
# Run specific test file
npm test -- src/components/Button.test.tsx
# Run tests with coverage report
npm run coverage
# Run tests in UI mode (interactive)
npm test -- --ui// src/components/Button.test.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import { describe, it, expect, vi } from 'vitest';
import Button from './Button';
describe('Button', () => {
it('renders with text', () => {
render(<Button>Click me</Button>);
expect(screen.getByText('Click me')).toBeInTheDocument();
});
it('calls onClick when clicked', () => {
const handleClick = vi.fn(); // Mock function
render(<Button onClick={handleClick}>Click me</Button>);
fireEvent.click(screen.getByText('Click me'));
expect(handleClick).toHaveBeenCalledTimes(1);
});
});import { render, screen, waitFor } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from 'react-query';
import { describe, it, expect } from 'vitest';
import MomentumDisplay from './MomentumDisplay';
import { gamificationApi } from '../api/gamificationApi';
import { vi } from 'vitest';
// Mock the API
vi.mock('../api/gamificationApi', () => ({
gamificationApi: {
getMomentum: vi.fn(),
},
}));
describe('MomentumDisplay', () => {
it('displays momentum after loading', async () => {
// Mock API response
(gamificationApi.getMomentum as any).mockResolvedValue({
momentum: 1500,
level: 5,
});
const queryClient = new QueryClient({
defaultOptions: { queries: { retry: false } },
});
render(
<QueryClientProvider client={queryClient}>
<MomentumDisplay />
</QueryClientProvider>
);
// Wait for loading to finish
await waitFor(() => {
expect(screen.getByText(/1500/)).toBeInTheDocument();
});
});
});import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { describe, it, expect, vi } from 'vitest';
import LoginForm from './LoginForm';
describe('LoginForm', () => {
it('submits form with valid data', async () => {
const user = userEvent.setup();
const onSubmit = vi.fn();
render(<LoginForm onSubmit={onSubmit} />);
// Fill form
await user.type(screen.getByLabelText(/email/i), 'test@example.com');
await user.type(screen.getByLabelText(/password/i), 'password123');
// Submit
await user.click(screen.getByRole('button', { name: /sign in/i }));
// Verify
await waitFor(() => {
expect(onSubmit).toHaveBeenCalledWith({
email: 'test@example.com',
password: 'password123',
});
});
});
it('shows validation errors', async () => {
render(<LoginForm />);
const submitButton = screen.getByRole('button', { name: /sign in/i });
fireEvent.click(submitButton);
await waitFor(() => {
expect(screen.getByText(/email is required/i)).toBeInTheDocument();
});
});
});We have helper functions in src/utils/testHelpers.tsx:
import { renderWithProviders, mockUser } from '../utils/testHelpers';
import { screen } from '@testing-library/react';
// Use the helper instead of manual setup
test('component with providers', () => {
renderWithProviders(<MyComponent />);
// Automatically wrapped with Router, QueryClient, Contexts
});import { vi } from 'vitest';
import { authApi } from '../api/authApi';
// Mock the entire module
vi.mock('../api/authApi', () => ({
authApi: {
login: vi.fn(),
register: vi.fn(),
},
}));
// In your test:
(authApi.login as any).mockResolvedValue({
success: true,
data: { user: mockUser, token: 'token' },
});import { waitFor } from '@testing-library/react';
test('async operation', async () => {
render(<AsyncComponent />);
// Wait for element to appear
await waitFor(() => {
expect(screen.getByText('Loaded!')).toBeInTheDocument();
});
});import { AuthProvider } from '../contexts/AuthContext';
test('with auth context', () => {
render(
<AuthProvider>
<MyComponent />
</AuthProvider>
);
});import { render } from '@testing-library/react';
import { expect, test } from 'vitest';
test('component snapshot', () => {
const { container } = render(<MyComponent />);
expect(container).toMatchSnapshot();
});Test how multiple components work together:
describe('Login Flow Integration', () => {
it('completes full login process', async () => {
const user = userEvent.setup();
renderWithProviders(<App />);
// Navigate to login
await user.click(screen.getByText('Sign In'));
// Fill and submit form
await user.type(screen.getByLabelText(/email/i), 'test@example.com');
await user.type(screen.getByLabelText(/password/i), 'password');
await user.click(screen.getByRole('button', { name: /sign in/i }));
// Verify redirect to dashboard
await waitFor(() => {
expect(screen.getByText(/dashboard/i)).toBeInTheDocument();
});
});
});- Aim for 70%+ coverage on critical paths
- Focus on: User flows, error handling, edge cases
- Don't obsess over: 100% coverage (not worth the time)
View coverage:
npm run coverage
# Opens coverage report in browser-
Use
screen.debug():render(<MyComponent />); screen.debug(); // Prints entire DOM to console screen.debug(screen.getByRole('button')); // Print specific element
-
Use
--reporter=verbose:npm test -- --reporter=verbose -
Add
console.login tests (they show in test output) -
Use
--uimode:npm test -- --ui # Interactive test runner with debugging tools
// src/components/UserCard.tsx
import React from 'react';
interface UserCardProps {
name: string;
email: string;
avatar?: string;
onEdit?: () => void;
}
export default function UserCard({ name, email, avatar, onEdit }: UserCardProps) {
return (
<div className="user-card">
{avatar && <img src={avatar} alt={name} />}
<h3>{name}</h3>
<p>{email}</p>
{onEdit && <button onClick={onEdit}>Edit</button>}
</div>
);
}/* src/styles/UserCard.css */
.user-card {
padding: 1rem;
border: 1px solid #ddd;
border-radius: 8px;
}
.user-card img {
width: 64px;
height: 64px;
border-radius: 50%;
}// src/components/UserCard.test.tsx
import { render, screen } from '@testing-library/react';
import { describe, it, expect, vi } from 'vitest';
import UserCard from './UserCard';
describe('UserCard', () => {
it('renders user information', () => {
render(<UserCard name="Alice" email="alice@example.com" />);
expect(screen.getByText('Alice')).toBeInTheDocument();
expect(screen.getByText('alice@example.com')).toBeInTheDocument();
});
});- Keep components small - One responsibility per component
- Use TypeScript - Define props interface
- Extract logic to hooks - Keep components focused on rendering
- Handle loading/error states:
if (isLoading) return <Spinner />; if (error) return <ErrorMessage error={error} />; return <Content />;
Browse src/components/ to see what's available:
import Button from '../components/Button';
import Input from '../components/Input';
import { MomentumBar } from '../components/gamification/MomentumBar';
function MyPage() {
return (
<div>
<Input label="Name" />
<Button onClick={handleClick}>Submit</Button>
<MomentumBar momentum={1500} />
</div>
);
}import { useQuery, useMutation } from 'react-query';
import { gamificationApi } from '../api/gamificationApi';
function MomentumDisplay() {
// Fetch data
const { data, isLoading, error } = useQuery(
'momentum',
() => gamificationApi.getMomentum()
);
// Update data
const mutation = useMutation(
(newMomentum: number) => gamificationApi.updateMomentum(newMomentum),
{
onSuccess: () => {
// Refetch momentum after update
queryClient.invalidateQueries('momentum');
},
}
);
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<p>Momentum: {data.momentum}</p>
<button onClick={() => mutation.mutate(2000)}>
Update Momentum
</button>
</div>
);
}import axiosInstance from '../api/axiosInstance';
async function fetchUserData(userId: string) {
try {
const response = await axiosInstance.get(`/users/${userId}`);
return response.data;
} catch (error) {
console.error('Failed to fetch user:', error);
throw error;
}
}Each feature has its own API file:
// src/api/gamificationApi.ts
import axiosInstance from './axiosInstance';
export const gamificationApi = {
getMomentum: async () => {
const response = await axiosInstance.get('/gamification/momentum');
return response.data;
},
updateMomentum: async (amount: number) => {
const response = await axiosInstance.post('/gamification/momentum', { amount });
return response.data;
},
};import { useQuery } from 'react-query';
import toast from 'react-hot-toast';
function MyComponent() {
const { data, error } = useQuery(
'myData',
fetchData,
{
onError: (error) => {
toast.error('Failed to load data');
console.error(error);
},
}
);
// Component code...
}Extract reusable logic:
// src/hooks/useMomentum.ts
import { useQuery } from 'react-query';
import { gamificationApi } from '../api/gamificationApi';
export function useMomentum() {
return useQuery('momentum', () => gamificationApi.getMomentum());
}
// Usage:
function MyComponent() {
const { data: momentum } = useMomentum();
// ...
}import { useForm } from 'react-hook-form';
function LoginForm() {
const { register, handleSubmit, formState: { errors } } = useForm();
const onSubmit = async (data: any) => {
try {
await authApi.login(data.email, data.password);
} catch (error) {
toast.error('Login failed');
}
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input
{...register('email', { required: 'Email is required' })}
type="email"
/>
{errors.email && <span>{errors.email.message}</span>}
<input
{...register('password', { required: 'Password is required' })}
type="password"
/>
{errors.password && <span>{errors.password.message}</span>}
<button type="submit">Sign In</button>
</form>
);
}import { Navigate } from 'react-router-dom';
import { useAuth } from '../contexts/AuthContext';
function ProtectedRoute({ children }: { children: React.ReactNode }) {
const { isAuthenticated, loading } = useAuth();
if (loading) return <div>Loading...</div>;
if (!isAuthenticated) return <Navigate to="/login" />;
return <>{children}</>;
}
// Usage in App.tsx:
<Route
path="/dashboard"
element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
}
/>function DataDisplay() {
const { data, isLoading, isError } = useQuery('data', fetchData);
if (isLoading) {
return <div className="spinner">Loading...</div>;
}
if (isError) {
return <div className="error">Failed to load data</div>;
}
return <div>{data.content}</div>;
}const mutation = useMutation(updateData, {
onMutate: async (newData) => {
// Cancel outgoing refetches
await queryClient.cancelQueries('data');
// Snapshot previous value
const previous = queryClient.getQueryData('data');
// Optimistically update
queryClient.setQueryData('data', newData);
return { previous };
},
onError: (err, newData, context) => {
// Rollback on error
queryClient.setQueryData('data', context.previous);
},
});Solution:
# Clear node_modules and reinstall
rm -rf node_modules package-lock.json
npm installSolution:
# Kill process on port 5173
# Windows:
netstat -ano | findstr :5173
taskkill /PID <PID> /F
# Mac/Linux:
lsof -ti:5173 | xargs kill -9
# Or use a different port:
npm run dev -- --port 3000Solution:
# Check TypeScript config
npm run type-check
# Common fixes:
# 1. Restart TypeScript server in VS Code (Cmd+Shift+P β "TypeScript: Restart TS Server")
# 2. Check tsconfig.json paths
# 3. Ensure types are installed: npm install --save-dev @types/reactSolution:
- Check browser console for errors
- Try hard refresh:
Ctrl+Shift+R(Windows) /Cmd+Shift+R(Mac) - Clear browser cache
- Restart dev server
Solution:
- Check Network tab in DevTools
- Verify API URL in
.envfile - Check if backend is running
- Verify CORS settings on backend
- Check authentication token in Application β Local Storage
Solution:
# Clear test cache
npm test -- --clearCache
# Run with verbose output
npm test -- --reporter=verbose
# Check if mocks are set up correctly
# Verify testHelpers are imported- React: https://react.dev/learn
- TypeScript: https://www.typescriptlang.org/docs/
- Vite: https://vitejs.dev/guide/
- React Query: https://tanstack.com/query/latest
- React Router: https://reactrouter.com/
- Vitest: https://vitest.dev/
- Week 1: React basics, JSX, components, props
- Week 2: Hooks (useState, useEffect), event handling
- Week 3: React Router, forms, API calls
- Week 4: TypeScript, testing, advanced patterns
- React Tutorial: https://react.dev/learn/tutorial-tic-tac-toe
- TypeScript Exercises: https://typescript-exercises.github.io/
- Testing Library Docs: https://testing-library.com/docs/react-testing-library/intro/
- Check
FRONTEND_DEBUG_GUIDE.mdfor debugging tips - Check
HMR_GUIDE.mdfor Hot Module Replacement details - Check
PERFORMANCE_OPTIMIZATION.mdfor performance tips
npm run dev # Start dev server
npm run build # Build for production
npm test # Run tests
npm run lint # Check code quality
npm run type-check # Check TypeScript// React
import { useState, useEffect } from 'react';
// React Router
import { useNavigate, useParams, Link } from 'react-router-dom';
// React Query
import { useQuery, useMutation } from 'react-query';
// Testing
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { vi, describe, it, expect } from 'vitest';// State
const [value, setValue] = useState(initial);
// Effect
useEffect(() => { /* code */ }, [dependencies]);
// Query
const { data, isLoading } = useQuery('key', fetchFn);
// Mutation
const mutation = useMutation(updateFn);
mutation.mutate(data);- Check this README first - Most common questions are answered here
- Search existing issues - Someone might have had the same problem
- Ask in team chat - We're here to help!
- Check browser console - Errors often have helpful messages
- Use React DevTools - Inspect component state and props
- β Set up your development environment
- β Run the app locally
- β Explore the codebase
- β Read through a component file
- β Make a small change and see it update
- β Write your first test
- β Create a simple component
Welcome to the team! You've got this! π
Last updated: 2024 Questions? Ask the team or update this README with your findings!