Skip to main content

Posts

Think like pro in React

this mindset shift is what makes the difference between just “coding React” and being a React developer who can own features end-to-end . Here’s a structured way to think whenever you’re given new functionality: 🧠 Step 1: Understand the Requirement What exactly is the feature supposed to do? (inputs, outputs, user flow) Who is the user, and where in the app will this be used? Clarify edge cases (e.g., what if API fails, what if no data, what if user is not logged in?). 👉 Example: If asked to “add search to product list,” clarify: Is it local search (filtering existing data) or API search (fetching from server)? Should it be instant (as you type) or only on Enter/click? What should happen if no results are found? 🧠 Step 2: Break Down into Components Which UI components are needed? (buttons, input fields, list, card, modal, etc.) Can I reuse existing components or do I need new ones? Where will state live? (local state with useState , lifted up to parent, or glo...

⚡ Part 1: Introduction to Generics in C#

🌍 Why Do We Need Generics? Imagine you want to create a stack (like a pile of books 📚): You can push items on top You can pop items off the top If we write a stack for integers : public class IntStack { private int[] items = new int[10]; private int index = 0; public void Push(int item) => items[index++] = item; public int Pop() => items[--index]; } 👉 Problem: This only works for int . What if we want a string stack ? Or a Customer stack ? We’d have to duplicate code for every type. 😢 ✅ Solution: Generics Generics let us create type-safe reusable code without duplication. We can say: “I don’t care what type it is yet — I’ll decide later.” 1) Generic Classes Here’s a generic stack : // Generic class "Stack<T>" // The <T> is a placeholder for any type public class Stack<T> { private T[] items = new T[10]; // Array of type T private int index = 0; // Push adds an item of type T public void P...

⚡ Part 4: Complex Scenarios with Generics in C#

In Part 1–3 , we learned: Basics of generics (methods, classes, constraints) Advanced patterns (delegates, factories, strategies) Real-world use cases (repository, service, caching, API responses) Now in Part 4 , we’ll handle trickier topics : Nested Generics Covariance & Contravariance Generic Methods in LINQ-style APIs Performance Considerations with Generics Common Pitfalls and Best Practices 1) Nested Generics Sometimes we need a generic type inside another generic type . This looks confusing at first, but it’s actually quite powerful. Example: Dictionary of Lists // A dictionary where the key is string, and the value is a list of integers Dictionary<string, List<int>> studentMarks = new(); studentMarks["Alice"] = new List<int> { 90, 85, 88 }; studentMarks["Bob"] = new List<int> { 70, 75, 80 }; foreach (var kv in studentMarks) { Console.WriteLine($"{kv.Key}: {string.Join(", ", k...

⚡ Part 3 : Real-World Use Cases of Generics in C#

in Part 3, we’ll apply generics to real-world enterprise scenarios: Generic Repository Pattern Generic Service Layer Generic Caching Utility Generic API Response Wrapper Let’s dive in 🚀 1) Generic Repository Pattern The repository pattern is used to separate data access logic (like Add , Get , Remove ) from the rest of the code. Without a generic repository, you’d write separate repositories for each entity (CustomerRepository, OrderRepository, etc.), which leads to a lot of duplicate code . Generics solve this by creating one reusable repository for all entities. Code Walkthrough // All entities will implement IEntity so that we can guarantee they have an Id property public interface IEntity { int Id { get; set; } } 👉 Why? Because in most systems, every object (Customer, Order, Product) has an Id that uniquely identifies it. By enforcing IEntity , we make sure our repository can always look up objects by Id. // Repository interface with CRUD-like operations p...

⚡ Part 2: Advanced Generics in C#

In Part 1 , we explored the basics of generics : Generic methods Generic classes Constraints Interfaces and real-world examples Now in Part 2 , we’ll dive deeper into advanced scenarios where generics really shine. We’ll cover: Generic Delegates & Events Generic Abstract Classes Generic Comparers & Equality Checkers Factory Pattern with Generics Strategy Pattern with Generics Let’s go step by step 🚀 1) Generic Delegates & Events A delegate is like a pointer to a method — it stores a reference to a function so we can call it later. When we make delegates generic , we can reuse them for multiple data types. Example: Generic Delegate // A delegate that takes two parameters of type T and returns T // This can represent operations like Add, Multiply, etc. public delegate T Operation<T>(T a, T b); class Calculator { // A generic Add method that works for numbers // Constraint: "where T : struct" ensures only...

🧱 Part 17 — Advanced TypeScript Patterns (Branded Types, Results, Exhaustive Checks) 🧠

When your app grows, types become your safety net . Here are advanced—but approachable—TypeScript patterns that prevent entire classes of bugs. Every snippet is commented and copy‑paste ready. 1) Branded (Opaque) Types — prevent mixing IDs/units Avoid passing a ProjectId where a TaskId is expected. // src/types/brand.ts // Create an opaque subtype of T (compile-time only) export type Brand<T, B extends string> = T & { readonly __brand: B }; export type TaskId = Brand<string, 'TaskId'>; export type ProjectId = Brand<string, 'ProjectId'>; export const TaskId = (s: string): TaskId => s as TaskId; export const ProjectId = (s: string): ProjectId => s as ProjectId; // Usage function loadTask(id: TaskId) {/* impl */} // loadTask('123'); // ❌ plain string not allowed loadTask(TaskId('123')); // ✅ branded Brands exist only at compile time; runtime values are plain strings. 2) Discriminated Unions + Exhaustiv...

🧱 Part 16 — Accessibility First (Semantics, Keyboard, ARIA, Focus, Color)

Accessibility (a11y) is not optional : it helps keyboard users, screen‑reader users, and frankly everyone (think: low contrast screens, bright sun, fatigue). This part is beginner‑friendly and packed with commented patterns you can paste in today. 🎯 Goals Semantic HTML & landmarks (nav, main, header, footer) Keyboard support (Tab order, focus outlines, ESC to close) Visible focus with :focus-visible Skip link , visually hidden text, live regions Readable colors (contrast), reduced motion ARIA for dialog , tabs , and form errors 1) Baseline: semantic layout + skip link Make the structure obvious to assistive tech and provide a quick jump to content. // src/app/Layout.tsx import { SkipLink } from '@/components/a11y/SkipLink'; export default function Layout({ children }: { children: React.ReactNode }) { return ( <div> <SkipLink target="#main" /> <header role="banner" className="p-4 border-b...

🧱 Part 15 — Code Splitting & Suspense (Lazy Routes, Error Boundaries, Preloading, Skeletons) 🧩

Big bundles make apps feel sluggish. Code splitting ships only what’s needed when it’s needed. Suspense gives you a clean way to show fallbacks (spinners/skeletons) while chunks/data load. This guide is beginner‑friendly and copy‑paste safe. 🎯 What you’ll do Split code at route and component level with React.lazy Wrap chunks in Suspense with polished fallbacks Add Error Boundaries so chunk or render failures don’t blank your app Preload chunks on hover/touch for instant nav Use Vite goodies like import.meta.glob for large lazy trees Show skeleton UIs instead of jumpy spinners Works with Vite + React Router 6+ (what we’ve been using). 1) Route‑level code splitting with React.lazy Each route becomes its own chunk. React loads it on demand. // src/app/AppRouter.tsx import { BrowserRouter, Routes, Route } from 'react-router-dom'; import { Suspense, lazy } from 'react'; import Layout from '@/app/Layout'; import NotFound from '@/pages...

🧱 Part 14 — Performance Tuning (memo, useCallback, Profiler, Virtualization) ⚡️

Make your app feel snappy by avoiding unnecessary work. This part is practical, beginner‑friendly, and packed with commented examples you can paste in. 🎛️ The Big Ideas Render less: memoize components and values so React doesn’t re-render needlessly. Do less: avoid heavy computations during render; cache them with useMemo . Work smarter: virtualize long lists so the DOM only contains visible rows. Measure first: use the React Profiler (and browser Performance tab) to verify improvements. Golden rule: Measure → Change → Measure again. 1) React.memo — skip re-renders when props didn’t change // src/components/perf/ExpensiveRow.tsx import React from 'react'; type RowProps = { id: string; title: string; done: boolean; onToggle: (id: string) => void; }; function RowBase({ id, title, done, onToggle }: RowProps) { // pretend this is heavy (expensive formatting, etc.) for (let i = 0; i < 200_000; i++); // demo CPU work return ( <div ...

🧱 Part 13 — Testing Strategy (Vitest + Testing Library + MSW) 🧪

A great test setup is fast, reliable, and mirrors how users actually use your app. We’ll set up Vitest (fast runner), React Testing Library (test behavior, not internals), and MSW (mock server) — with clear, commented examples for components , hooks , and Redux slices , plus API integration tests. Philosophy: Test the public surface (what users see/do), not implementation details. Prefer queries by role/label/text over test IDs. Keep unit tests small; add a few integration tests where flows matter. 1) Install test tooling pnpm add -D vitest @testing-library/react @testing-library/user-event @testing-library/jest-dom jsdom pnpm add -D msw whatwg-fetch # MSW for API mocks, fetch polyfill for jsdom 2) Configure Vitest (Vite + jsdom + setup file) // vitest.config.ts import { defineConfig } from 'vitest/config'; import react from '@vitejs/plugin-react'; export default defineConfig({ plugins: [react()], test: { environment: 'jsdom...

🧱 Part 12 — Storybook for React + TypeScript (Document & Test Your UI) 📚

Now that we have design system primitives (Button, Input, Card), we need a way to document, preview, and test them in isolation. That’s where Storybook comes in. Storybook provides: A component playground outside your app. Interactive docs for your design system. Addons for accessibility (a11y) , viewport testing , and interaction testing . 1) Install Storybook Run the official setup tool: npx storybook@latest init Detects React + TypeScript + Vite automatically. Creates a .storybook/ folder with config. Adds example stories in src/stories/ . Start Storybook: pnpm storybook This launches on http://localhost:6006 . 2) Configure TypeScript support In .storybook/tsconfig.json , extend your root config: { "extends": "../tsconfig.json", "include": ["../src", "./*.ts", "./*.tsx"] } 3) Write your first story (Button) Stories live alongside components or in src/stories/ . Each story is a different s...

🧱 Part 11 — Design System & Theming (Tokens • CSS Variables • Tailwind • A11y) 🎨

A design system makes your UI consistent, accessible, and fast to iterate. We’ll define design tokens (colors, spacing, radii, typography) as CSS variables, wire light/dark themes , and build typed UI primitives (Button, Input, Card). Everything is copy‑paste ready and verified for errors. 🧩 Concepts in 30 seconds Tokens : the single source of truth for colors/spacing/typography. Primitives : small, reusable components that read tokens. Variants : controlled style options (e.g., primary | secondary | danger ). Theming : switch token values for light/dark without changing component code. 1) Tokens as CSS variables (light & dark) We use HSL/hex via var(--token) so classes can read them with Tailwind’s arbitrary values. /* src/styles/tokens.css */ :root { /* Base colors */ --color-bg: #ffffff; --color-fg: #111111; --color-muted: #6b7280; /* slate-500 */ --color-primary: #2563eb; /* blue-600 */ --color-danger: #dc2626; /* red-600 *...