/** * React + TypeScript Demo Page * Demonstrates modern React patterns with TypeScript */ import { useState, useEffect, useCallback, useMemo, useRef } from 'react'; import { Link } from 'react-router-dom'; import { useAppDispatch, useAppSelector } from '@/store'; import { selectTheme, selectResolvedTheme, setTheme } from '@/store/slices/uiSlice'; // Type definitions interface User { id: number; name: string; email: string; role: 'admin' | 'user' | 'guest'; } interface CounterState { count: number; lastUpdated: Date; } // Custom hook example function useLocalStorage(key: string, initialValue: T): [T, (value: T) => void] { const [storedValue, setStoredValue] = useState(() => { try { const item = window.localStorage.getItem(key); return item ? JSON.parse(item) : initialValue; } catch { return initialValue; } }); const setValue = (value: T) => { setStoredValue(value); window.localStorage.setItem(key, JSON.stringify(value)); }; return [storedValue, setValue]; } // Custom hook for debouncing function useDebounce(value: T, delay: number): T { const [debouncedValue, setDebouncedValue] = useState(value); useEffect(() => { const handler = setTimeout(() => setDebouncedValue(value), delay); return () => clearTimeout(handler); }, [value, delay]); return debouncedValue; } export default function ReactTypeScriptDemo() { // Redux for actual theme toggling const dispatch = useAppDispatch(); const theme = useAppSelector(selectTheme); const resolvedTheme = useAppSelector(selectResolvedTheme); // useState with types const [counter, setCounter] = useState({ count: 0, lastUpdated: new Date(), }); const [users, setUsers] = useState([ { id: 1, name: 'Alice', email: 'alice@example.com', role: 'admin' }, { id: 2, name: 'Bob', email: 'bob@example.com', role: 'user' }, { id: 3, name: 'Charlie', email: 'charlie@example.com', role: 'guest' }, ]); const [searchTerm, setSearchTerm] = useState(''); const debouncedSearch = useDebounce(searchTerm, 300); // useRef with types const inputRef = useRef(null); const renderCount = useRef(0); // useCallback with types const increment = useCallback(() => { setCounter(prev => ({ count: prev.count + 1, lastUpdated: new Date(), })); }, []); const decrement = useCallback(() => { setCounter(prev => ({ count: Math.max(0, prev.count - 1), lastUpdated: new Date(), })); }, []); // useMemo with types const filteredUsers = useMemo(() => { return users.filter(user => user.name.toLowerCase().includes(debouncedSearch.toLowerCase()) || user.email.toLowerCase().includes(debouncedSearch.toLowerCase()) ); }, [users, debouncedSearch]); const userStats = useMemo(() => ({ total: users.length, admins: users.filter(u => u.role === 'admin').length, regularUsers: users.filter(u => u.role === 'user').length, guests: users.filter(u => u.role === 'guest').length, }), [users]); // Custom hook usage (demo of useLocalStorage pattern) const [persistedValue, setPersistedValue] = useLocalStorage('demo-value', 'Hello!'); // Track renders - use state for display since refs shouldn't be read during render const [displayRenderCount, setDisplayRenderCount] = useState(0); useEffect(() => { renderCount.current += 1; setDisplayRenderCount(renderCount.current); }); // Focus input on mount useEffect(() => { inputRef.current?.focus(); }, []); // Add user handler const addUser = () => { const newUser: User = { id: Date.now(), name: `User ${users.length + 1}`, email: `user${users.length + 1}@example.com`, role: 'user', }; setUsers(prev => [...prev, newUser]); }; // Remove user handler const removeUser = (id: number) => { setUsers(prev => prev.filter(u => u.id !== id)); }; // Toggle role handler const toggleRole = (id: number) => { setUsers(prev => prev.map(u => { if (u.id !== id) return u; const roles: User['role'][] = ['admin', 'user', 'guest']; const currentIndex = roles.indexOf(u.role); const nextRole = roles[(currentIndex + 1) % roles.length]; return { ...u, role: nextRole }; })); }; return (
← Back to Home

React + TypeScript Demo

Demonstrating modern React patterns with full TypeScript support.

{/* Counter with useState */}

useState with Types

{counter.count}

Last updated: {counter.lastUpdated.toLocaleTimeString()}

{`const [counter, setCounter] = useState({
  count: 0,
  lastUpdated: new Date(),
});`}
          
{/* Custom Hooks */}

Custom Hooks

Theme (Redux): Current: {theme} (resolved: {resolvedTheme})
Persisted value: setPersistedValue(e.target.value)} className="input w-48" placeholder="Type something..." /> (Survives page refresh)
{`function useLocalStorage(key: string, initialValue: T): [T, (value: T) => void] {
  const [storedValue, setStoredValue] = useState(() => {
    const item = window.localStorage.getItem(key);
    return item ? JSON.parse(item) : initialValue;
  });
  // ...
}`}
          
{/* useRef */}

useRef with Types

Component has rendered {displayRenderCount} times

{`const inputRef = useRef(null);
const renderCount = useRef(0);

useEffect(() => {
  inputRef.current?.focus();
}, []);`}
          
{/* useMemo & useCallback */}

useMemo & useCallback

setSearchTerm(e.target.value)} placeholder="Search users (debounced)..." className="input" />
Total: {userStats.total} Admins: {userStats.admins} Users: {userStats.regularUsers} Guests: {userStats.guests}
{filteredUsers.map(user => (
{user.name} {user.email}
))}
{`const filteredUsers = useMemo(() => {
  return users.filter(user =>
    user.name.toLowerCase().includes(debouncedSearch.toLowerCase())
  );
}, [users, debouncedSearch]);

const increment = useCallback(() => {
  setCounter(prev => ({ ...prev, count: prev.count + 1 }));
}, []);`}
          
{/* Type Definitions */}

Type Definitions

{`interface User {
  id: number;
  name: string;
  email: string;
  role: 'admin' | 'user' | 'guest';  // Union type
}

interface CounterState {
  count: number;
  lastUpdated: Date;
}

// Generic custom hook
function useLocalStorage(key: string, initialValue: T): [T, (value: T) => void]`}
          
); }