Zum Inhalt springen

React Commands

React is a JavaScript library for building user interfaces with reusable components.

Project Setup

Create React App

# Using Create React App
npx create-react-app my-app
cd my-app
npm start

# With TypeScript
npx create-react-app my-app --template typescript

# With custom template
npx create-react-app my-app --template cra-template-typescript
# Create Vite project
npm create vite@latest my-app -- --template react

# With TypeScript
npm create vite@latest my-app -- --template react-ts

# Install and run
cd my-app
npm install
npm run dev

Development Commands

CommandDescription
npm startStart development server
npm run devStart Vite dev server
npm testRun tests
npm run buildBuild for production
npm run lintRun ESLint
npm run previewPreview production build

React Hooks

useState

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

import { useEffect, useState } from 'react';

function DataFetcher() {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch('/api/data')
      .then(res => res.json())
      .then(data => {
        setData(data);
        setLoading(false);
      });
  }, []); // Dependency array

  return loading ? <p>Loading...</p> : <div>{JSON.stringify(data)}</div>;
}

useContext

import { createContext, useContext } from 'react';

const ThemeContext = createContext();

export function useTheme() {
  return useContext(ThemeContext);
}

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <Header />
    </ThemeContext.Provider>
  );
}

function Header() {
  const theme = useTheme();
  return <h1>Theme: {theme}</h1>;
}

useReducer

import { useReducer } from 'react';

interface State {
  count: number;
}

type Action = { type: 'INCREMENT' } | { type: 'DECREMENT' };

function reducer(state: State, action: Action): State {
  switch (action.type) {
    case 'INCREMENT':
      return { count: state.count + 1 };
    case 'DECREMENT':
      return { count: state.count - 1 };
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, { count: 0 });

  return (
    <div>
      <p>{state.count}</p>
      <button onClick={() => dispatch({ type: 'INCREMENT' })}>+</button>
      <button onClick={() => dispatch({ type: 'DECREMENT' })}>-</button>
    </div>
  );
}

useRef

import { useRef } from 'react';

function TextInput() {
  const inputRef = useRef<HTMLInputElement>(null);

  const handleClick = () => {
    inputRef.current?.focus();
  };

  return (
    <>
      <input ref={inputRef} />
      <button onClick={handleClick}>Focus Input</button>
    </>
  );
}

useMemo & useCallback

import { useMemo, useCallback } from 'react';

function Parent() {
  const [count, setCount] = useState(0);

  // Memoized value
  const expensiveValue = useMemo(() => {
    return count * 2;
  }, [count]);

  // Memoized callback
  const handleClick = useCallback(() => {
    setCount(c => c + 1);
  }, []);

  return <Child value={expensiveValue} onClick={handleClick} />;
}

Custom Hooks

function useFetch(url: string) {
  const [data, setData] = useState(null);
  const [error, setError] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch(url)
      .then(res => res.json())
      .then(data => {
        setData(data);
        setLoading(false);
      })
      .catch(err => {
        setError(err);
        setLoading(false);
      });
  }, [url]);

  return { data, error, loading };
}

// Usage
function App() {
  const { data, error, loading } = useFetch('/api/users');
  return loading ? <p>Loading...</p> : <div>{JSON.stringify(data)}</div>;
}

Component Patterns

Functional Component

interface Props {
  name: string;
  age: number;
  onClick?: (name: string) => void;
}

function User({ name, age, onClick }: Props) {
  return (
    <div onClick={() => onClick?.(name)}>
      <h2>{name}</h2>
      <p>Age: {age}</p>
    </div>
  );
}

export default User;

Controlled Component

function Form() {
  const [formData, setFormData] = useState({
    name: '',
    email: '',
    message: ''
  });

  const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
    const { name, value } = e.target;
    setFormData(prev => ({
      ...prev,
      [name]: value
    }));
  };

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    console.log(formData);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input name="name" value={formData.name} onChange={handleChange} />
      <input name="email" value={formData.email} onChange={handleChange} />
      <textarea name="message" value={formData.message} onChange={handleChange} />
      <button type="submit">Submit</button>
    </form>
  );
}

List Rendering

interface Item {
  id: number;
  name: string;
}

function ItemList({ items }: { items: Item[] }) {
  return (
    <ul>
      {items.map(item => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
}

Conditional Rendering

function Alert({ type, message }: { type: 'error' | 'success'; message: string }) {
  if (!message) return null;

  const className = type === 'error' ? 'alert-error' : 'alert-success';

  return (
    <div className={className}>
      {message}
    </div>
  );
}

// Using ternary
function Status({ isActive }: { isActive: boolean }) {
  return isActive ? <p>Active</p> : <p>Inactive</p>;
}

// Using &&
function Message({ count }: { count: number }) {
  return count > 0 && <p>You have {count} messages</p>;
}

State Management

Context API

interface AppContextType {
  user: User | null;
  setUser: (user: User) => void;
}

const AppContext = createContext<AppContextType | undefined>(undefined);

export function useAppContext() {
  const context = useContext(AppContext);
  if (!context) {
    throw new Error('useAppContext must be used within AppProvider');
  }
  return context;
}

export function AppProvider({ children }: { children: React.ReactNode }) {
  const [user, setUser] = useState<User | null>(null);

  return (
    <AppContext.Provider value={{ user, setUser }}>
      {children}
    </AppContext.Provider>
  );
}

Forms with React Hook Form

import { useForm } from 'react-hook-form';

interface FormData {
  name: string;
  email: string;
}

function MyForm() {
  const { register, handleSubmit, formState: { errors } } = useForm<FormData>();

  const onSubmit = (data: FormData) => {
    console.log(data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register('name', { required: 'Name is required' })} />
      {errors.name && <span>{errors.name.message}</span>}

      <input {...register('email', { required: 'Email is required' })} />
      {errors.email && <span>{errors.email.message}</span>}

      <button type="submit">Submit</button>
    </form>
  );
}

Styling

Inline Styles

const styles = {
  container: {
    padding: '20px',
    backgroundColor: '#f0f0f0'
  },
  title: {
    fontSize: '24px',
    color: '#333'
  }
};

function App() {
  return (
    <div style={styles.container}>
      <h1 style={styles.title}>Hello</h1>
    </div>
  );
}

CSS Modules

// App.module.css
.container {
  padding: 20px;
  background-color: #f0f0f0;
}

// App.tsx
import styles from './App.module.css';

function App() {
  return <div className={styles.container}>Hello</div>;
}

Tailwind CSS

function App() {
  return (
    <div className="flex items-center justify-center min-h-screen bg-gray-100">
      <div className="bg-white p-8 rounded-lg shadow-lg">
        <h1 className="text-3xl font-bold text-gray-800">Hello</h1>
      </div>
    </div>
  );
}

Testing

Unit Tests with Jest & React Testing Library

import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import App from './App';

describe('App', () => {
  test('renders heading', () => {
    render(<App />);
    expect(screen.getByText(/hello/i)).toBeInTheDocument();
  });

  test('increments counter on button click', async () => {
    render(<Counter />);
    const button = screen.getByRole('button', { name: /increment/i });

    await userEvent.click(button);
    expect(screen.getByText('1')).toBeInTheDocument();
  });
});

Performance Optimization

React.memo

interface ItemProps {
  name: string;
}

const Item = React.memo(({ name }: ItemProps) => {
  console.log('Item rendered:', name);
  return <div>{name}</div>;
});

Code Splitting with Suspense

import { Suspense, lazy } from 'react';

const Dashboard = lazy(() => import('./pages/Dashboard'));

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Dashboard />
    </Suspense>
  );
}

Common Patterns

Error Boundary

class ErrorBoundary extends React.Component<
  { children: React.ReactNode },
  { hasError: boolean }
> {
  constructor(props: any) {
    super(props);
    this.state = { hasError: false };
  }

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

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

Portal

import { createPortal } from 'react-dom';

function Modal({ children }: { children: React.ReactNode }) {
  return createPortal(
    <div className="modal">{children}</div>,
    document.getElementById('modal-root')!
  );
}

Best Practices

  • Use functional components and hooks
  • Keep components small and focused
  • Lift state up only when necessary
  • Use composition over inheritance
  • Implement proper error handling
  • Use TypeScript for type safety
  • Memoize expensive computations
  • Lazy load routes and code
  • Test components thoroughly
  • Follow naming conventions

Useful Libraries

  • React Query: Data fetching and caching
  • Redux: State management
  • React Router: Client-side routing
  • Next.js: Full-stack framework
  • Material-UI: Component library
  • Styled Components: CSS-in-JS

Resources


Last updated: 2026-03-30|React 18+