Tutorials Logic, IN +91 8092939553 info@tutorialslogic.com
FAQs Support
Navigation
Home About Us Contact Us Blogs FAQs
Tutorials
All Tutorials
Services
Academic Projects Resume Writing Interview Questions Website Development
Compiler Tutorials

React with TypeScript

Why TypeScript with React?

TypeScript adds static typing to React, catching errors at compile time rather than runtime. It provides excellent IDE support, makes refactoring safer, and serves as living documentation for your components.

React TypeScript — Props, Hooks, Events, Generics
// TypedComponents.tsx
import { ReactNode, MouseEvent, ChangeEvent, FC } from 'react'

// Interface for props
interface ButtonProps {
    label: string
    variant?: 'primary' | 'secondary' | 'danger'
    size?: 'sm' | 'md' | 'lg'
    disabled?: boolean
    loading?: boolean
    onClick?: (event: MouseEvent<HTMLButtonElement>) => void
    children?: ReactNode
}

// Typed function component
const Button: FC<ButtonProps> = ({
    label,
    variant = 'primary',
    size = 'md',
    disabled = false,
    loading = false,
    onClick,
    children
}) => {
    return (
        <button
            className={`btn btn-${variant} btn-${size}`}
            disabled={disabled || loading}
            onClick={onClick}
        >
            {loading ? 'Loading...' : (children || label)}
        </button>
    )
}

// Typed input component
interface InputProps {
    label: string
    value: string
    onChange: (value: string) => void
    type?: 'text' | 'email' | 'password' | 'number'
    error?: string
    required?: boolean
}

function Input({ label, value, onChange, type = 'text', error, required }: InputProps) {
    const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
        onChange(e.target.value)
    }

    return (
        <div>
            <label>{label}{required && ' *'}</label>
            <input type={type} value={value} onChange={handleChange} />
            {error && <span className="error">{error}</span>}
        </div>
    )
}

// Generic component
interface ListProps<T> {
    items: T[]
    renderItem: (item: T, index: number) => ReactNode
    keyExtractor: (item: T) => string | number
    emptyMessage?: string
}

function List<T>({ items, renderItem, keyExtractor, emptyMessage = 'No items' }: ListProps<T>) {
    if (items.length === 0) return <p>{emptyMessage}</p>
    return (
        <ul>
            {items.map((item, index) => (
                <li key={keyExtractor(item)}>{renderItem(item, index)}</li>
            ))}
        </ul>
    )
}

// Usage
const users = [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]
<List
    items={users}
    keyExtractor={u => u.id}
    renderItem={u => <span>{u.name}</span>}
/>
// TypedHooks.tsx
import { useState, useRef, useEffect, useCallback } from 'react'

// Typed useState
const [count, setCount] = useState<number>(0)
const [user, setUser] = useState<User | null>(null)
const [items, setItems] = useState<string[]>([])

// Typed useRef
const inputRef = useRef<HTMLInputElement>(null)
const timerRef = useRef<ReturnType<typeof setTimeout>>(null)

// Access DOM element
inputRef.current?.focus()

// Typed useCallback
const handleClick = useCallback((id: number): void => {
    setItems(prev => prev.filter((_, i) => i !== id))
}, [])

// Typed custom hook
function useFetch<T>(url: string) {
    const [data, setData] = useState<T | null>(null)
    const [loading, setLoading] = useState(true)
    const [error, setError] = useState<string | null>(null)

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

    return { data, loading, error }
}

// Usage with type inference
const { data: users } = useFetch<User[]>('/api/users')
// users is typed as User[] | null

// Typed event handlers
function handleInputChange(e: React.ChangeEvent<HTMLInputElement>): void {
    console.log(e.target.value)
}

function handleFormSubmit(e: React.FormEvent<HTMLFormElement>): void {
    e.preventDefault()
}

function handleKeyDown(e: React.KeyboardEvent<HTMLInputElement>): void {
    if (e.key === 'Enter') console.log('Enter pressed')
}

function handleMouseEnter(e: React.MouseEvent<HTMLDivElement>): void {
    console.log(e.clientX, e.clientY)
}
// types.ts — shared type definitions

export type UserRole = 'admin' | 'user' | 'moderator'

export interface User {
    id: number
    name: string
    email: string
    role: UserRole
    avatar?: string
    createdAt: string
    isActive: boolean
}

export interface ApiResponse<T> {
    data: T
    message: string
    success: boolean
    pagination?: Pagination
}

export interface Pagination {
    page: number
    perPage: number
    total: number
    totalPages: number
}

// Utility types
export type CreateUser = Omit<User, 'id' | 'createdAt'>
export type UpdateUser = Partial<Omit<User, 'id'>>
export type UserPreview = Pick<User, 'id' | 'name' | 'avatar'>

// Component prop types
export interface TableColumn<T> {
    key: keyof T
    header: string
    sortable?: boolean
    render?: (value: T[keyof T], row: T) => React.ReactNode
}

// Hook return types
export interface UseFormReturn<T> {
    values: T
    errors: Partial<Record<keyof T, string>>
    handleChange: (field: keyof T, value: T[keyof T]) => void
    handleSubmit: (onSubmit: (values: T) => void) => (e: React.FormEvent) => void
    reset: () => void
    isValid: boolean
}

Ready to Level Up Your Skills?

Explore 500+ free tutorials across 20+ languages and frameworks.