Skip to content

Instantly share code, notes, and snippets.

@mosioc
Last active June 14, 2026 16:17
Show Gist options
  • Select an option

  • Save mosioc/a7bea5cf998b0c151140a3b0f0c18110 to your computer and use it in GitHub Desktop.

Select an option

Save mosioc/a7bea5cf998b0c151140a3b0f0c18110 to your computer and use it in GitHub Desktop.
React.js Main Concepts

React.js Main Concepts (JavaScript Version)

The TypeScript version is in the comments (recommended).

Core Fundamentals

  • Components – Building blocks of a React app (function or class-based).
  • JSX (JavaScript XML) – Syntax extension that allows mixing HTML with JavaScript.
  • Props (Properties) – Used to pass data from parent to child components.
  • State – Data that changes over time within a component.
  • Rendering – How React updates the DOM when state or props change.
  • Virtual DOM – Lightweight copy of the real DOM for efficient updates.
  • Reconciliation – The process React uses to update the DOM efficiently.

Hooks (Functional Component Features)

  • useState – Manages component state.
  • useEffect – Performs side effects (e.g., fetching data, adding event listeners).
  • useContext – Accesses global data from React Context.
  • useRef – References DOM elements or persists values across renders.
  • useMemo – Memoizes computed values to avoid unnecessary recalculations.
  • useCallback – Memoizes functions to prevent unnecessary re-creations.
  • useReducer – Manages complex state logic with reducers (like Redux).
  • Custom Hooks – Reusable logic encapsulated in custom functions.

Component Communication

  • Parent → Child: via props
  • Child → Parent: via callback functions
  • Sibling → Sibling: via lifting state up or context

Context API & State Management

  • Context API – For global state management (avoiding prop drilling).
  • Reducers – Pure functions for state updates.
  • Redux / Zustand / Recoil / Jotai – External state management libraries.

Routing

  • React Router – For navigation between pages (SPA behavior).
  • Route, Link, Navigate, Outlet – Core components of routing.

Styling

  • CSS Modules – Scoped CSS for components.
  • Styled Components / Emotion – CSS-in-JS libraries.
  • Tailwind CSS – Utility-first styling framework.

Component Patterns

  • Controlled vs Uncontrolled Components – Managing form inputs.
  • Higher-Order Components (HOCs) – Functions that enhance components.
  • Render Props – Sharing logic via render functions.
  • Compound Components – Components designed to work together.
  • Container & Presentational Pattern – Separation of logic and UI.

Performance Optimization

  • React.memo – Prevents re-rendering of unchanged components.
  • Lazy Loading & Suspense – Code-splitting and dynamic imports.
  • Profiler – Analyzes rendering performance.

Advanced Concepts

  • Portals – Render children into a DOM node outside the parent hierarchy.
  • Error Boundaries – Catch JavaScript errors in component trees.
  • Refs & Forwarding Refs – Access and pass refs through components.
  • Concurrent Rendering (React 18) – Improves UI responsiveness.
  • Transitions (useTransition) – Marks state updates as non-urgent.
  • Server Components (React 18+) – Rendering parts of UI on the server.

Ecosystem & Tools

  • Next.js / Remix – Frameworks built on top of React.
  • Vite / CRA – React app bootstrapping tools.
  • React DevTools – Browser extension for debugging.

Chapter 1: Taking Your First Steps with React

Declarative vs Imperative Programming

  • Imperative: Describes how things work (step-by-step instructions)
  • Declarative: Describes what you want to achieve (React's approach)

React Elements

// JSX creates elements
<Title color="red">
  <h1>Hello, H1!</h1>
</Title>

// Transpiles to:
{
  type: Title,
  props: {
    color: 'red',
    children: {
      type: 'h1',
      props: { children: 'Hello, H1!' }
    }
  }
}

Key Concepts

  • React uses Virtual DOM for efficient updates
  • Components are reusable building blocks
  • Elements are immutable descriptions of UI
  • JSX is syntactic sugar for React.createElement()

Setting Up with Vite

npm install -g create-vite
create-vite my-react-app --template react-ts
cd my-react-app
npm install
npm run dev

Chapter 2: Introducing TypeScript

Basic Types

// Primitive types
const name: string = 'Carlos'
const age: number = 35
const active: boolean = true

// Type definition
type User = {
  username: string
  email: string
  age?: number  // Optional property
}

Interfaces

interface IUser {
  username: string
  email: string
  age?: number
}

// Extending interfaces
interface IPerson extends IWork {
  name: string
  age: number
}

Enums

enum Colors {
  PRIMARY = '#FF0000',
  SECONDARY = '#00FF00',
  TERTIARY = '#0000FF'
}

Template Literal Types

type Theme = 'light' | 'dark'

tsconfig.json Essentials

{
  "compilerOptions": {
    "target": "ESNext",
    "jsx": "react-jsx",
    "strict": true,
    "esModuleInterop": true
  }
}

Chapter 3: Cleaning Up Your Code

JSX Basics

// Props
<img src="..." alt="..." />

// Children
<Button>Click me!</Button>

// Expressions in JSX
<div>Hello, {name}!</div>

// Style objects
<div style={{ backgroundColor: 'red' }} />

JSX vs HTML Differences

  • className instead of class
  • htmlFor instead of for
  • camelCase for event handlers: onClick, onChange
  • Self-closing tags required: <img />

Conditional Rendering

// Using &&
{isLoggedIn && <LogoutButton />}

// Ternary operator
{isLoggedIn ? <LogoutButton /> : <LoginButton />}

// Helper function
const canShowData = () => dataIsReady && (isAdmin || userHasPermissions)
{canShowData() && <SecretData />}

Lists and Keys

<ul>
  {users.map(user => (
    <li key={user.id}>{user.name}</li>
  ))}
</ul>

ESLint Configuration

{
  "extends": ["airbnb", "prettier"],
  "rules": {
    "semi": [2, "never"],
    "max-len": ["error", { "code": 100 }]
  }
}

Functional Programming Concepts

// Pure function
const add = (x, y) => x + y

// Immutability
const add3 = arr => arr.concat(3)  // ✓ Good
const add3 = arr => arr.push(3)     // ✗ Bad (mutates)

// Currying
const add = x => y => x + y
const add1 = add(1)
add1(2) // 3

// Composition
const addAndSquare = (x, y) => square(add(x, y))

Chapter 4: Exploring Popular Composition Patterns

Children Prop

const Button = ({ children }) => (
  <button className="btn">{children}</button>
)

<Button>
  <img src="..." />
  <span>Click me!</span>
</Button>

Container and Presentational Pattern

// Container (logic)
const GeolocationContainer = () => {
  const [latitude, setLatitude] = useState(null)
  const [longitude, setLongitude] = useState(null)

  useEffect(() => {
    navigator.geolocation.getCurrentPosition(handleSuccess)
  }, [])

  return <Geolocation latitude={latitude} longitude={longitude} />
}

// Presentational (UI)
const Geolocation = ({ latitude, longitude }) => (
  <div>
    <div>Latitude: {latitude}</div>
    <div>Longitude: {longitude}</div>
  </div>
)

Higher-Order Components (HOCs)

const withClassName = Component => props => (
  <Component {...props} className="my-class" />
)

const MyComponentWithClassName = withClassName(MyComponent)

Function as Child Pattern

const Name = ({ children }) => children('World')

<Name>
  {name => <div>Hello, {name}!</div>}
</Name>

Return Statement

  • () => { ... } → Need return
  • () => ( ... ) → Implicit return
  • () => <div>...</div> → Implicit return

Chapter 5: Writing Code for the Browser

Controlled Components

const [values, setValues] = useState({ firstName: '', lastName: '' })

const handleChange = ({ target: { name, value } }) => {
  setValues({ ...values, [name]: value })
}

<input
  name="firstName"
  value={values.firstName}
  onChange={handleChange}
/>

Event Handling

const handleEvent = (event) => {
  switch (event.type) {
    case 'click':
      console.log('clicked')
      break
    case 'dblclick':
      console.log('double clicked')
      break
  }
}

<button onClick={handleEvent} onDoubleClick={handleEvent}>
  Click me!
</button>

Refs

const inputRef = useRef(null)

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

<input type="text" ref={inputRef} />
<button onClick={handleClick}>Set Focus</button>

forwardRef

const TextInputWithRef = React.forwardRef((props, ref) => (
  <input ref={ref} type="text" {...props} />
))

// Usage
const inputRef = useRef()
<TextInputWithRef ref={inputRef} />

Chapter 6: Making Your Components Look Beautiful

Inline Styles

const style = {
  color: 'palevioletred',
  backgroundColor: 'papayawhip',
  fontSize: 16  // Numbers default to px
}

<button style={style}>Click me!</button>

CSS Modules

/* Button.module.css */
.button {
  background-color: #ff0000;
  padding: 20px;
}
import styles from './Button.module.css'
<button className={styles.button}>Click me!</button>

Styled Components

import styled from 'styled-components'

const Button = styled.button`
  background-color: #ff0000;
  padding: 20px;
  border-radius: 5px;
  
  &:hover {
    color: #fff;
  }
  
  @media (max-width: 480px) {
    width: 160px;
  }
`

<Button>Click me!</Button>

Chapter 7: Anti-Patterns to Be Avoided

❌ Initializing State with Props

// BAD
const [count, setCount] = useState(props.count)

// GOOD - make it explicit
const [count, setCount] = useState(props.initialCount)

❌ Using Index as Key

// BAD
{items.map((item, index) => (
  <li key={index}>{item}</li>
))}

// GOOD
{items.map((item) => (
  <li key={item.id}>{item}</li>
))}

❌ Spreading Props on DOM Elements

// BAD
<div {...props} />

// GOOD
<div {...props.domProps} />

Chapter 8: React Hooks

useState

const [counter, setCounter] = useState(0)

const handleCounter = (operation) => {
  if (operation === 'add') {
    setCounter(counter + 1)
  } else {
    setCounter(counter - 1)
  }
}

useEffect

// componentDidMount
useEffect(() => {
  // runs once
}, [])

// componentDidUpdate
useEffect(() => {
  // runs on every render
})

// with dependencies
useEffect(() => {
  // runs when dependencies change
}, [dependency1, dependency2])

// cleanup
useEffect(() => {
  return () => {
    // cleanup code
  }
}, [])

useCallback

const handleDelete = useCallback((taskId) => {
  const newTodoList = todoList.filter(todo => todo.id !== taskId)
  setTodoList(newTodoList)
}, [todoList])

useMemo

const filteredTodoList = useMemo(() => 
  todoList.filter(todo => 
    todo.task.toLowerCase().includes(term.toLowerCase())
  ),
  [term, todoList]
)

React.memo

const MyComponent = memo(({ name }) => (
  <div>{name}</div>
))

useReducer

const reducer = (state, action) => {
  switch (action.type) {
    case 'ADD':
      return [...state, action.payload]
    case 'DELETE':
      return state.filter(item => item.id !== action.payload)
    default:
      return state
  }
}

const [state, dispatch] = useReducer(reducer, initialState)

dispatch({ type: 'ADD', payload: newItem })

Rules of Hooks

  1. Only call Hooks at the top level
  2. Only call Hooks from React functions

Chapter 9: React Router

Basic Setup

npm install react-router-dom @types/react-router-dom
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'

<Router>
  <Routes>
    <Route path="/" element={<Home />} />
    <Route path="/about" element={<About />} />
    <Route path="*" element={<Error404 />} />
  </Routes>
</Router>

Route Parameters

// Define route
<Route path="/contacts/:contactId" element={<Contact />} />

// Access params
const { contactId } = useParams()

Navigation

import { Link } from 'react-router-dom'

<Link to="/about">About</Link>

React Router v6.4 Loaders

// Loader function
export const dataLoader = async () => {
  const response = await fetch('https://api.example.com/data')
  return response.json()
}

// Route with loader
<Route 
  path="/pokemons" 
  element={<Pokemons />} 
  loader={dataLoader} 
/>

// In component
const data = useLoaderData()
const navigation = useNavigation()

if (navigation.state === 'loading') {
  return <div>Loading...</div>
}

Chapter 10: React 18 New Features

Concurrent Mode

  • Time slicing for better responsiveness
  • Suspense for data fetching
  • Concurrent rendering

Automatic Batching

// React 18 batches these updates automatically
setCount(count + 1)
setCount(count + 1)
setCount(count + 1)
// Results in single render

Transitions

import { useTransition } from 'react'

const [isPending, startTransition] = useTransition()

startTransition(() => {
  setSearchTerm(value)
})

Suspense

<Suspense fallback={<Loading />}>
  <UserProfile />
</Suspense>

New APIs

// createRoot
import { createRoot } from 'react-dom/client'
createRoot(document.getElementById('root')).render(<App />)

// hydrateRoot (for SSR)
import { hydrateRoot } from 'react-dom/client'
hydrateRoot(document.getElementById('root'), <App />)

New Hooks

// useId - for unique IDs
const id = useId()

// useTransition
const [isPending, startTransition] = useTransition()

// useDeferredValue
const deferredValue = useDeferredValue(value)

// useInsertionEffect (for CSS-in-JS)
useInsertionEffect(() => {
  // Insert styles
}, [])

Chapter 11: Managing Data

React Context API

// Create context
export const UserContext = createContext({
  user: null,
  login: () => null
})

// Provider
const UserProvider = ({ children }) => {
  const [user, setUser] = useState(null)
  
  const login = async (credentials) => {
    // login logic
  }
  
  return (
    <UserContext.Provider value={{ user, login }}>
      {children}
    </UserContext.Provider>
  )
}

// Consumer
const { user, login } = useContext(UserContext)

SWR (Stale-While-Revalidate)

import useSWR from 'swr'

const fetcher = (url) => fetch(url).then(res => res.json())

const { data, error } = useSWR('/api/user', fetcher)

if (error) return <div>Failed to load</div>
if (!data) return <div>Loading...</div>
return <div>Hello {data.name}!</div>

Redux Toolkit

// Create slice
import { createSlice } from '@reduxjs/toolkit'

const counterSlice = createSlice({
  name: 'counter',
  initialState: 0,
  reducers: {
    increment: state => state + 1,
    decrement: state => state - 1
  }
})

// Configure store
import { configureStore } from '@reduxjs/toolkit'

const store = configureStore({
  reducer: {
    counter: counterReducer
  }
})

// Use in components
const count = useSelector(state => state.counter)
const dispatch = useDispatch()
dispatch(increment())

Chapter 12: Server-Side Rendering

Basic SSR with Express

import express from 'express'
import { renderToString } from 'react-dom/server'
import App from './App'

const app = express()

app.get('/', (req, res) => {
  const body = renderToString(<App />)
  const html = `
    <!DOCTYPE html>
    <html>
      <body>
        <div id="root">${body}</div>
        <script src="/bundle.js"></script>
      </body>
    </html>
  `
  res.send(html)
})

app.listen(3000)

Data Fetching in SSR

// Server
app.get('/', async (req, res) => {
  const data = await fetchData()
  const body = renderToString(<App data={data} />)
  const html = template(body, data)
  res.send(html)
})

// Template with dehydration
const template = (body, data) => `
  <div id="root">${body}</div>
  <script>window.__DATA__ = ${JSON.stringify(data)}</script>
  <script src="/bundle.js"></script>
`

// Client hydration
const data = window.__DATA__
ReactDOM.hydrateRoot(
  document.getElementById('root'),
  <App data={data} />
)

Next.js Setup

npm install next react react-dom
// pages/index.js
export async function getServerSideProps() {
  const data = await fetchData()
  return { props: { data } }
}

export default function Home({ data }) {
  return <div>{data.title}</div>
}

Chapter 13: Understanding GraphQL

GraphQL Schema

graphql

# Scalar types
scalar UUID
scalar Datetime

# Type definition
type User {
  id: UUID!
  username: String!
  email: String!
  role: String!
  active: Boolean!
}

# Query
type Query {
  getUser(at: String!): User!
  getUsers: [User!]
}

# Mutation
type Mutation {
  createUser(input: CreateUserInput): User!
  login(input: LoginInput): Token!
}

# Input types
input CreateUserInput {
  username: String!
  password: String!
  email: String!
}

Apollo Server Setup

import { ApolloServer } from '@apollo/server'
import { expressMiddleware } from '@apollo/server/express4'

const apolloServer = new ApolloServer({
  typeDefs,
  resolvers
})

await apolloServer.start()

app.use(
  '/graphql',
  cors(),
  json(),
  expressMiddleware(apolloServer, {
    context: async () => ({ models })
  })
)

Resolvers

export default {
  Query: {
    getUsers: (_, __, { models }) => 
      models.User.findAll(),
    
    getUser: async (_, { at }, { models }) => {
      const user = await getUserData(at)
      return user
    }
  },
  Mutation: {
    createUser: (_, { input }, { models }) =>
      models.User.create({ ...input }),
    
    login: (_, { input }, { models }) =>
      doLogin(input.email, input.password, models)
  }
}

Apollo Client

import { ApolloClient, ApolloProvider, gql, useQuery, useMutation } from '@apollo/client'

const client = new ApolloClient({
  uri: 'http://localhost:4000/graphql',
  cache: new InMemoryCache()
})

<ApolloProvider client={client}>
  <App />
</ApolloProvider>

// Query
const GET_USERS = gql`
  query GetUsers {
    getUsers {
      id
      username
      email
    }
  }
`

const { data, loading, error } = useQuery(GET_USERS)

// Mutation
const LOGIN = gql`
  mutation Login($email: String!, $password: String!) {
    login(input: { email: $email, password: $password }) {
      token
    }
  }
`

const [login] = useMutation(LOGIN)

Chapter 14: MonoRepo Architecture

NPM Workspaces Setup

// Root package.json
{
  "name": "web-creator",
  "private": true,
  "workspaces": [
    "packages/*"
  ]
}

Package Structure

root/
├── packages/
│   ├── api/
│   │   └── package.json (@web-creator/api)
│   ├── frontend/
│   │   └── package.json (@web-creator/frontend)
│   └── utils/
│       └── package.json (@web-creator/utils)
└── package.json

Shared TypeScript Config

// tsconfig.common.json
{
  "compilerOptions": {
    "target": "ESNext",
    "jsx": "react-jsx",
    "strict": true,
    "esModuleInterop": true
  }
}

// Package tsconfig.json
{
  "extends": "../../tsconfig.common.json",
  "compilerOptions": {
    "outDir": "./dist"
  }
}

Webpack Config for Packages

// webpack.common.ts
export default (args) => ({
  entry: `./src/index.ts`,
  output: {
    path: resolve(__dirname, `../../../${packageName}/dist`),
    filename: 'index.js'
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js'],
    alias: {
      '~': resolve(__dirname, `../../../${packageName}/src`)
    }
  }
})

Chapter 15: Improving Performance

Reconciliation and Keys

// Good - stable unique keys
{items.map(item => (
  <li key={item.id}>{item.name}</li>
))}

// Bad - using index
{items.map((item, index) => (
  <li key={index}>{item.name}</li>
))}

React.memo

const ExpensiveComponent = memo(({ data }) => {
  // Expensive calculations
  return <div>{data}</div>
})

useMemo for Expensive Calculations

const expensiveValue = useMemo(() => {
  return computeExpensiveValue(a, b)
}, [a, b])

useCallback for Functions

const memoizedCallback = useCallback(() => {
  doSomething(a, b)
}, [a, b])

Code Splitting

import { lazy, Suspense } from 'react'

const LazyComponent = lazy(() => import('./LazyComponent'))

<Suspense fallback={<Loading />}>
  <LazyComponent />
</Suspense>

Production Build

// webpack.config.js
new webpack.DefinePlugin({
  'process.env.NODE_ENV': JSON.stringify('production')
})

// Split bundles
optimization: {
  splitChunks: {
    cacheGroups: {
      vendor: {
        test: /node_modules/,
        name: 'vendor',
        chunks: 'all'
      }
    }
  }
}

Chapter 16: Testing and Debugging

Jest Setup

npm install --save-dev jest @testing-library/react @testing-library/jest-dom
// jest.config.js
module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'jsdom',
  setupFilesAfterEnv: ['<rootDir>/setupTests.ts']
}

Basic Component Test

import { render, screen } from '@testing-library/react'

describe('Hello Component', () => {
  it('should render Hello World', () => {
    render(<Hello />)
    expect(screen.getByText('Hello World')).toBeInTheDocument()
  })
  
  it('should render the name prop', () => {
    render(<Hello name="Carlos" />)
    expect(screen.getByText('Hello Carlos')).toBeInTheDocument()
  })
})

Testing Events

import { fireEvent } from '@testing-library/react'

it('should handle click', () => {
  const { container } = render(<ShowInformation />)
  const button = container.querySelector('button')
  
  fireEvent.click(button)
  
  expect(container.querySelector('.info')).toBeInTheDocument()
})

Vitest Setup

npm install -D vitest @vitest/ui @testing-library/react

typescript

// vite.config.ts
export default defineConfig({
  test: {
    environment: 'jsdom',
    globals: true
  }
})

React DevTools

  • Install Chrome extension
  • Inspect component tree
  • View props and state
  • Profile performance

Redux DevTools

import { composeWithDevTools } from 'redux-devtools-extension'

const store = createStore(
  rootReducer,
  composeWithDevTools(applyMiddleware(...middleware))
)

Chapter 17: Deploying to Production

DigitalOcean Droplet Setup

# SSH into droplet
ssh root@YOUR_DROPLET_IP

# Install Node.js
curl -sL https://deb.nodesource.com/setup_19.x -o nodesource_setup.sh
sudo bash nodesource_setup.sh
sudo apt install nodejs -y

# Install PM2
npm install -g pm2

# Install nginx
sudo apt-get update
sudo apt-get install nginx

Nginx Configuration

nginx

# /etc/nginx/sites-available/default
location / {
  proxy_pass http://localhost:3000;
  proxy_http_version 1.1;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection 'upgrade';
  proxy_set_header Host $host;
  proxy_cache_bypass $http_upgrade;
}

PM2 Process Management

# Start app
pm2 start npm --name "app" -- start

# View logs
pm2 logs

# Restart
pm2 restart app

# Stop
pm2 stop app

CircleCI Configuration

# .circleci/config.yml
version: 2.1
jobs:
  build:
    docker:
      - image: cimg/node:18.12.1
    steps:
      - checkout
      - run: npm install
      - run: npm run lint
      - run: npm test
      - run: ssh $DROPLET_USER@$DROPLET_IP 'cd app; git pull; npm install; pm2 restart app'

workflows:
  build-deploy:
    jobs:
      - build:
          filters:
            branches:
              only: master

Quick Reference

Common Hooks

Hook	Purpose	ExampleuseState	Manage state	const [state, setState] = useState(0)
useEffect	Side effects	useEffect(() => { }, [deps])
useContext	Context consumer	const value = useContext(MyContext)
useRef	DOM reference	const ref = useRef(null)
useMemo	Memoize value	useMemo(() => compute(), [deps])
useCallback	Memoize function	useCallback(() => fn(), [deps])
useReducer	Complex state	const [state, dispatch] = useReducer(reducer, init)

Performance Tips

  • ✅ Use React.memo for expensive components
  • ✅ Use useMemo for expensive calculations
  • ✅ Use useCallback for functions passed as props
  • ✅ Use proper keys in lists
  • ✅ Code split with React.lazy
  • ❌ Don't optimize prematurely
  • ❌ Don't use index as key
  • ❌ Don't mutate state directly

Best Practices

  • Write small, focused components
  • Keep components pure when possible
  • Use TypeScript for type safety
  • Test components thoroughly
  • Follow ESLint rules
  • Use meaningful variable names
  • Document complex logic
  • Keep state as local as possible
@mosioc

mosioc commented Jun 13, 2026

Copy link
Copy Markdown
Author

React.js Main Concepts (TypeScript)

Core Fundamentals

  • Components – Building blocks of a React app (function or class-based).
  • JSX (JavaScript XML) – Syntax extension that allows mixing HTML with JavaScript.
  • Props (Properties) – Used to pass data from parent to child components.
  • State – Data that changes over time within a component.
  • Rendering – How React updates the DOM when state or props change.
  • Virtual DOM – Lightweight copy of the real DOM for efficient updates.
  • Reconciliation – The process React uses to update the DOM efficiently.

Hooks (Functional Component Features)

  • useState – Manages component state.
  • useEffect – Performs side effects (e.g., fetching data, adding event listeners).
  • useContext – Accesses global data from React Context.
  • useRef – References DOM elements or persists values across renders.
  • useMemo – Memoizes computed values to avoid unnecessary recalculations.
  • useCallback – Memoizes functions to prevent unnecessary re-creations.
  • useReducer – Manages complex state logic with reducers (like Redux).
  • Custom Hooks – Reusable logic encapsulated in custom functions.

Component Communication

  • Parent → Child: via props
  • Child → Parent: via callback functions
  • Sibling → Sibling: via lifting state up or context

Context API & State Management

  • Context API – For global state management (avoiding prop drilling).
  • Reducers – Pure functions for state updates.
  • Redux / Zustand / Recoil / Jotai – External state management libraries.

Routing

  • React Router – For navigation between pages (SPA behavior).
  • Route, Link, Navigate, Outlet – Core components of routing.

Styling

  • CSS Modules – Scoped CSS for components.
  • Styled Components / Emotion – CSS-in-JS libraries.
  • Tailwind CSS – Utility-first styling framework.

Component Patterns

  • Controlled vs Uncontrolled Components – Managing form inputs.
  • Higher-Order Components (HOCs) – Functions that enhance components.
  • Render Props – Sharing logic via render functions.
  • Compound Components – Components designed to work together.
  • Container & Presentational Pattern – Separation of logic and UI.

Performance Optimization

  • React.memo – Prevents re-rendering of unchanged components.
  • Lazy Loading & Suspense – Code-splitting and dynamic imports.
  • Profiler – Analyzes rendering performance.

Advanced Concepts

  • Portals – Render children into a DOM node outside the parent hierarchy.
  • Error Boundaries – Catch JavaScript errors in component trees.
  • Refs & Forwarding Refs – Access and pass refs through components.
  • Concurrent Rendering (React 18) – Improves UI responsiveness.
  • Transitions (useTransition) – Marks state updates as non-urgent.
  • Server Components (React 18+) – Rendering parts of UI on the server.

Ecosystem & Tools

  • Next.js / Remix – Frameworks built on top of React.
  • Vite / CRA – React app bootstrapping tools.
  • React DevTools – Browser extension for debugging.

Chapter 1: Taking Your First Steps with React

Declarative vs Imperative Programming

  • Imperative: Describes how things work (step-by-step instructions)
  • Declarative: Describes what you want to achieve (React's approach)

React Elements

// JSX creates elements
<Title color="red">
  <h1>Hello, H1!</h1>
</Title>

// Transpiles to:
{
  type: Title,
  props: {
    color: 'red',
    children: {
      type: 'h1',
      props: { children: 'Hello, H1!' }
    }
  }
}

Key Concepts

  • React uses Virtual DOM for efficient updates
  • Components are reusable building blocks
  • Elements are immutable descriptions of UI
  • JSX is syntactic sugar for React.createElement()

Setting Up with Vite

npm install -g create-vite
create-vite my-react-app --template react-ts
cd my-react-app
npm install
npm run dev

Chapter 2: Introducing TypeScript

Basic Types

// Primitive types
const name: string = 'Carlos'
const age: number = 35
const active: boolean = true

// Type definition
type User = {
  username: string
  email: string
  age?: number  // Optional property
}

Interfaces

interface IUser {
  username: string
  email: string
  age?: number
}

// Extending interfaces
interface IPerson extends IWork {
  name: string
  age: number
}

Enums

enum Colors {
  PRIMARY = '#FF0000',
  SECONDARY = '#00FF00',
  TERTIARY = '#0000FF'
}

Template Literal Types

type Theme = 'light' | 'dark'

tsconfig.json Essentials

{
  "compilerOptions": {
    "target": "ESNext",
    "jsx": "react-jsx",
    "strict": true,
    "esModuleInterop": true
  }
}

Chapter 3: Cleaning Up Your Code

JSX Basics

// Props
<img src="..." alt="..." />

// Children
<Button>Click me!</Button>

// Expressions in JSX
<div>Hello, {name}!</div>

// Style objects
<div style={{ backgroundColor: 'red' }} />

JSX vs HTML Differences

  • className instead of class
  • htmlFor instead of for
  • camelCase for event handlers: onClick, onChange
  • Self-closing tags required: <img />

Conditional Rendering

// Using &&
{isLoggedIn && <LogoutButton />}

// Ternary operator
{isLoggedIn ? <LogoutButton /> : <LoginButton />}

// Helper function
const canShowData = (): boolean => dataIsReady && (isAdmin || userHasPermissions)
{canShowData() && <SecretData />}

Lists and Keys

interface User {
  id: string
  name: string
}

<ul>
  {users.map((user: User) => (
    <li key={user.id}>{user.name}</li>
  ))}
</ul>

ESLint Configuration

{
  "extends": ["airbnb", "prettier"],
  "rules": {
    "semi": [2, "never"],
    "max-len": ["error", { "code": 100 }]
  }
}

Functional Programming Concepts

// Pure function
const add = (x: number, y: number): number => x + y

// Immutability
const add3 = (arr: number[]): number[] => arr.concat(3)  // ✓ Good
// const add3 = (arr: number[]): number => arr.push(3)     // ✗ Bad (mutates)

// Currying
const add = (x: number) => (y: number): number => x + y
const add1 = add(1)
add1(2) // 3

// Composition
const addAndSquare = (x: number, y: number): number => square(add(x, y))

Return Statement

  • () => { ... } → Need return
  • () => ( ... ) → Implicit return
  • () => <div>...</div> → Implicit return

Chapter 4: Exploring Popular Composition Patterns

Children Prop

interface ButtonProps {
  children: React.ReactNode
}

const Button: React.FC<ButtonProps> = ({ children }) => (
  <button className="btn">{children}</button>
)

<Button>
  <img src="..." alt="icon" />
  <span>Click me!</span>
</Button>

Container and Presentational Pattern

// Types
interface GeolocationProps {
  latitude: number | null
  longitude: number | null
}

// Container (logic)
const GeolocationContainer: React.FC = () => {
  const [latitude, setLatitude] = useState<number | null>(null)
  const [longitude, setLongitude] = useState<number | null>(null)

  useEffect(() => {
    navigator.geolocation.getCurrentPosition(
      (position: GeolocationPosition) => {
        setLatitude(position.coords.latitude)
        setLongitude(position.coords.longitude)
      }
    )
  }, [])

  return <Geolocation latitude={latitude} longitude={longitude} />
}

// Presentational (UI)
const Geolocation: React.FC<GeolocationProps> = ({ latitude, longitude }) => (
  <div>
    <div>Latitude: {latitude}</div>
    <div>Longitude: {longitude}</div>
  </div>
)

Higher-Order Components (HOCs)

interface WithClassNameProps {
  className?: string
}

const withClassName = <P extends object>(
  Component: React.ComponentType<P>
): React.FC<P & WithClassNameProps> => (props: P & WithClassNameProps) => (
  <Component {...props} className="my-class" />
)

const MyComponentWithClassName = withClassName(MyComponent)

Function as Child Pattern

interface NameProps {
  children: (name: string) => React.ReactNode
}

const Name: React.FC<NameProps> = ({ children }) => children('World')

<Name>
  {(name: string) => <div>Hello, {name}!</div>}
</Name>

Chapter 5: Writing Code for the Browser

Controlled Components

interface FormValues {
  firstName: string
  lastName: string
}

const [values, setValues] = useState<FormValues>({ firstName: '', lastName: '' })

const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
  const { name, value } = event.target
  setValues({ ...values, [name]: value })
}

<input
  name="firstName"
  value={values.firstName}
  onChange={handleChange}
/>

Event Handling

const handleEvent = (event: React.MouseEvent<HTMLButtonElement>) => {
  switch (event.type) {
    case 'click':
      console.log('clicked')
      break
    case 'dblclick':
      console.log('double clicked')
      break
  }
}

<button onClick={handleEvent} onDoubleClick={handleEvent}>
  Click me!
</button>

Refs

const inputRef = useRef<HTMLInputElement>(null)

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

<input type="text" ref={inputRef} />
<button onClick={handleClick}>Set Focus</button>

forwardRef

interface TextInputProps {
  type?: string
}

const TextInputWithRef = React.forwardRef<HTMLInputElement, TextInputProps>(
  (props, ref) => (
    <input ref={ref} type="text" {...props} />
  )
)

// Usage
const inputRef = useRef<HTMLInputElement>(null)
<TextInputWithRef ref={inputRef} />

Chapter 6: Making Your Components Look Beautiful

Inline Styles

const style: React.CSSProperties = {
  color: 'palevioletred',
  backgroundColor: 'papayawhip',
  fontSize: 16  // Numbers default to px
}

<button style={style}>Click me!</button>

CSS Modules

/* Button.module.css */
.button {
  background-color: #ff0000;
  padding: 20px;
}
import styles from './Button.module.css'
<button className={styles.button}>Click me!</button>

Styled Components

import styled from 'styled-components'

const Button = styled.button`
  background-color: #ff0000;
  padding: 20px;
  border-radius: 5px;
  
  &:hover {
    color: #fff;
  }
  
  @media (max-width: 480px) {
    width: 160px;
  }
`

<Button>Click me!</Button>

Chapter 7: Anti-Patterns to Be Avoided

❌ Initializing State with Props

interface CounterProps {
  count: number
  initialCount: number
}

// BAD
const [count, setCount] = useState(props.count)

// GOOD - make it explicit
const [count, setCount] = useState(props.initialCount)

❌ Using Index as Key

// BAD
{items.map((item: Item, index: number) => (
  <li key={index}>{item}</li>
))}

// GOOD
{items.map((item: Item) => (
  <li key={item.id}>{item}</li>
))}

❌ Spreading Props on DOM Elements

interface ComponentProps {
  domProps: React.HTMLAttributes<HTMLDivElement>
}

// BAD
<div {...props} />

// GOOD
<div {...props.domProps} />

Chapter 8: React Hooks

useState

const [counter, setCounter] = useState<number>(0)

const handleCounter = (operation: 'add' | 'subtract') => {
  if (operation === 'add') {
    setCounter(counter + 1)
  } else {
    setCounter(counter - 1)
  }
}

useEffect

// componentDidMount
useEffect(() => {
  // runs once
}, [])

// componentDidUpdate
useEffect(() => {
  // runs on every render
})

// with dependencies
useEffect(() => {
  // runs when dependencies change
}, [dependency1, dependency2])

// cleanup
useEffect(() => {
  return () => {
    // cleanup code
  }
}, [])

useCallback

interface Todo {
  id: string
  task: string
}

const handleDelete = useCallback((taskId: string) => {
  const newTodoList = todoList.filter((todo: Todo) => todo.id !== taskId)
  setTodoList(newTodoList)
}, [todoList])

useMemo

const filteredTodoList = useMemo(() => 
  todoList.filter((todo: Todo) => 
    todo.task.toLowerCase().includes(term.toLowerCase())
  ),
  [term, todoList]
)

React.memo

interface MyComponentProps {
  name: string
}

const MyComponent: React.FC<MyComponentProps> = memo(({ name }) => (
  <div>{name}</div>
))

useReducer

interface TodoItem {
  id: string
  task: string
}

type Action = 
  | { type: 'ADD'; payload: TodoItem }
  | { type: 'DELETE'; payload: string }

const reducer = (state: TodoItem[], action: Action): TodoItem[] => {
  switch (action.type) {
    case 'ADD':
      return [...state, action.payload]
    case 'DELETE':
      return state.filter(item => item.id !== action.payload)
    default:
      return state
  }
}

const [state, dispatch] = useReducer(reducer, initialState)

dispatch({ type: 'ADD', payload: newItem })

Rules of Hooks

  1. Only call Hooks at the top level
  2. Only call Hooks from React functions

Chapter 9: React Router

Basic Setup

npm install react-router-dom @types/react-router-dom
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'

const App: React.FC = () => (
  <Router>
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/about" element={<About />} />
      <Route path="*" element={<Error404 />} />
    </Routes>
  </Router>
)

Route Parameters

// Define route
<Route path="/contacts/:contactId" element={<Contact />} />

// Access params with typed useParams
const { contactId } = useParams<{ contactId: string }>()

Navigation

import { Link } from 'react-router-dom'

<Link to="/about">About</Link>

React Router v6.4 Loaders

interface PokemonData {
  id: number
  name: string
}

// Loader function
export const dataLoader = async (): Promise<PokemonData[]> => {
  const response = await fetch('https://api.example.com/data')
  return response.json()
}

// Route with loader
<Route 
  path="/pokemons" 
  element={<Pokemons />} 
  loader={dataLoader} 
/>

// In component
const data = useLoaderData() as PokemonData[]
const navigation = useNavigation()

if (navigation.state === 'loading') {
  return <div>Loading...</div>
}

Chapter 10: React 18 New Features

Concurrent Mode

  • Time slicing for better responsiveness
  • Suspense for data fetching
  • Concurrent rendering

Automatic Batching

// React 18 batches these updates automatically
setCount(count + 1)
setCount(count + 1)
setCount(count + 1)
// Results in single render

Transitions

import { useTransition } from 'react'

const [isPending, startTransition] = useTransition()

const handleSearch = (value: string) => {
  startTransition(() => {
    setSearchTerm(value)
  })
}

Suspense

<Suspense fallback={<Loading />}>
  <UserProfile />
</Suspense>

New APIs

// createRoot
import { createRoot } from 'react-dom/client'

const container = document.getElementById('root')
if (container) {
  const root = createRoot(container)
  root.render(<App />)
}

// hydrateRoot (for SSR)
import { hydrateRoot } from 'react-dom/client'

const container = document.getElementById('root')
if (container) {
  hydrateRoot(container, <App />)
}

New Hooks

// useId - for unique IDs
const id: string = useId()

// useTransition
const [isPending, startTransition] = useTransition()

// useDeferredValue
const deferredValue = useDeferredValue(value)

// useInsertionEffect (for CSS-in-JS)
useInsertionEffect(() => {
  // Insert styles
}, [])

Chapter 10.1: React 19 New Features

Key Features

  • Actions – Built-in form handling with pending states
  • Server Components – Stable, deeply integrated server-side rendering
  • Document Metadata – Native <title>, <meta>, <link> components
  • New HooksuseActionState, useFormStatus, useOptimistic
  • Asset Loading – Declarative stylesheet and script loading

Actions

async function updateUser(previousState: unknown, formData: FormData) {
  const name = formData.get('name') as string
  const response = await fetch('/api/user', {
    method: 'PUT',
    body: JSON.stringify({ name })
  })
  return response.json()
}

function UserForm(): JSX.Element {
  const [state, formAction, isPending] = useActionState(updateUser, null)

  return (
    <form action={formAction}>
      <input name="name" />
      <button type="submit" disabled={isPending}>
        {isPending ? 'Saving...' : 'Save'}
      </button>
      {state?.message && <p>{state.message}</p>}
    </form>
  )
}

useFormStatus

function SubmitButton(): JSX.Element {
  const { pending } = useFormStatus()
  
  return (
    <button type="submit" disabled={pending}>
      {pending ? 'Submitting...' : 'Submit'}
    </button>
  )
}

useOptimistic

interface Message {
  id: string
  text: string
  sending?: boolean
}

function Chat(): JSX.Element {
  const [messages, setMessages] = useState<Message[]>([])
  
  const [optimisticMessages, addOptimisticMessage] = useOptimistic(
    messages,
    (state: Message[], newText: string) => [
      ...state,
      { id: `temp-${Date.now()}`, text: newText, sending: true }
    ]
  )

  const formAction = async (formData: FormData) => {
    const text = formData.get('message') as string
    addOptimisticMessage(text)
    
    const newMsg = await sendMessage(text)
    setMessages(prev => [...prev, newMsg])
  }

  return (
    <form action={formAction}>
      {optimisticMessages.map(msg => (
        <div key={msg.id} className={msg.sending ? 'sending' : 'sent'}>
          {msg.text}
        </div>
      ))}
      <input name="message" />
    </form>
  )
}

Document Metadata

function ProductPage(): JSX.Element {
  return (
    <>
      <title>Wireless Headphones</title>
      <meta name="description" content="Premium noise-canceling headphones" />
      <meta property="og:title" content="Wireless Headphones" />
      <link rel="canonical" href="https://example.com/headphones" />
      
      <main>
        <h1>Wireless Headphones</h1>
      </main>
    </>
  )
}

Server Components

// UserList.server.tsx
export default async function UserList(): Promise<JSX.Element> {
  const users = await db.query('SELECT * FROM users')
  
  return (
    <ul>
      {users.map((user: User) => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  )
}

'use server' Directive

// actions.ts
'use server'

import { revalidatePath } from 'next/cache'

export async function createPost(formData: FormData): Promise<void> {
  await db.post.create({
    data: {
      title: formData.get('title'),
      content: formData.get('content')
    }
  })
  revalidatePath('/posts')
}
// PostForm.client.tsx
'use client'

import { createPost } from './actions'

function PostForm(): JSX.Element {
  return (
    <form action={createPost}>
      <input name="title" required />
      <textarea name="content" required />
      <button type="submit">Create Post</button>
    </form>
  )
}

Asset Loading

function OptimizedPage(): JSX.Element {
  return (
    <html>
      <head>
        <link rel="preconnect" href="https://api.example.com" />
        <link 
          rel="preload" 
          href="/fonts/main.woff2" 
          as="font" 
          crossOrigin="anonymous" 
        />
        <link 
          rel="stylesheet" 
          href="/styles/critical.css" 
          precedence="high" 
        />
      </head>
      <body>
        <main>
          <Suspense fallback={<Skeleton />}>
            <HeavyComponent />
          </Suspense>
        </main>
      </body>
    </html>
  )
}

Migration Tips

// ❌ Removed in React 19
ReactDOM.render(<App />, document.getElementById('root'))

// ✅ Use createRoot
import { createRoot } from 'react-dom/client'

const container = document.getElementById('root')
if (container) {
  createRoot(container).render(<App />)
}

Chapter 11: Managing Data

React Context API

interface User {
  name: string
  email: string
}

interface UserContextType {
  user: User | null
  login: (credentials: { email: string; password: string }) => Promise<void>
}

// Create context
export const UserContext = createContext<UserContextType>({
  user: null,
  login: async () => {}
})

// Provider
const UserProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
  const [user, setUser] = useState<User | null>(null)
  
  const login = async (credentials: { email: string; password: string }) => {
    // login logic
  }
  
  return (
    <UserContext.Provider value={{ user, login }}>
      {children}
    </UserContext.Provider>
  )
}

// Consumer
const { user, login } = useContext(UserContext)

SWR (Stale-While-Revalidate)

import useSWR from 'swr'

interface UserData {
  name: string
  email: string
}

const fetcher = (url: string): Promise<UserData> => 
  fetch(url).then(res => res.json())

const { data, error } = useSWR<UserData>('/api/user', fetcher)

if (error) return <div>Failed to load</div>
if (!data) return <div>Loading...</div>
return <div>Hello {data.name}!</div>

Redux Toolkit

// Create slice
import { createSlice, PayloadAction } from '@reduxjs/toolkit'

interface CounterState {
  value: number
}

const counterSlice = createSlice({
  name: 'counter',
  initialState: { value: 0 } as CounterState,
  reducers: {
    increment: (state) => {
      state.value += 1
    },
    decrement: (state) => {
      state.value -= 1
    }
  }
})

// Configure store
import { configureStore } from '@reduxjs/toolkit'

const store = configureStore({
  reducer: {
    counter: counterReducer
  }
})

export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch

// Use in components
const count = useSelector((state: RootState) => state.counter.value)
const dispatch = useDispatch<AppDispatch>()
dispatch(increment())

Chapter 12: Server-Side Rendering

Basic SSR with Express

import express from 'express'
import { renderToString } from 'react-dom/server'
import App from './App'

const app = express()

app.get('/', (req, res) => {
  const body = renderToString(<App />)
  const html = `
    <!DOCTYPE html>
    <html>
      <body>
        <div id="root">${body}</div>
        <script src="/bundle.js"></script>
      </body>
    </html>
  `
  res.send(html)
})

app.listen(3000)

Data Fetching in SSR

interface AppData {
  title: string
  content: string
}

// Server
app.get('/', async (req, res) => {
  const data: AppData = await fetchData()
  const body = renderToString(<App data={data} />)
  const html = template(body, data)
  res.send(html)
})

// Template with dehydration
const template = (body: string, data: AppData): string => `
  <div id="root">${body}</div>
  <script>window.__DATA__ = ${JSON.stringify(data)}</script>
  <script src="/bundle.js"></script>
`

// Client hydration
const data: AppData = window.__DATA__
const container = document.getElementById('root')
if (container) {
  ReactDOM.hydrateRoot(
    container,
    <App data={data} />
  )
}

Next.js Setup

npm install next react react-dom
// pages/index.tsx
import { GetServerSideProps } from 'next'

interface HomeProps {
  data: {
    title: string
  }
}

export const getServerSideProps: GetServerSideProps<HomeProps> = async () => {
  const data = await fetchData()
  return { props: { data } }
}

const Home: React.FC<HomeProps> = ({ data }) => {
  return <div>{data.title}</div>
}

export default Home

Chapter 13: Understanding GraphQL

GraphQL Schema

# Scalar types
scalar UUID
scalar Datetime

# Type definition
type User {
  id: UUID!
  username: String!
  email: String!
  role: String!
  active: Boolean!
}

# Query
type Query {
  getUser(at: String!): User!
  getUsers: [User!]
}

# Mutation
type Mutation {
  createUser(input: CreateUserInput): User!
  login(input: LoginInput): Token!
}

# Input types
input CreateUserInput {
  username: String!
  password: String!
  email: String!
}

Apollo Server Setup

import { ApolloServer } from '@apollo/server'
import { expressMiddleware } from '@apollo/server/express4'

const apolloServer = new ApolloServer({
  typeDefs,
  resolvers
})

await apolloServer.start()

app.use(
  '/graphql',
  cors(),
  json(),
  expressMiddleware(apolloServer, {
    context: async () => ({ models })
  })
)

Resolvers

interface UserModel {
  findAll: () => Promise<User[]>
  create: (input: CreateUserInput) => Promise<User>
}

interface Context {
  models: {
    User: UserModel
  }
}

export default {
  Query: {
    getUsers: (_: unknown, __: unknown, { models }: Context) => 
      models.User.findAll(),
    
    getUser: async (_: unknown, { at }: { at: string }, { models }: Context) => {
      const user = await getUserData(at)
      return user
    }
  },
  Mutation: {
    createUser: (_: unknown, { input }: { input: CreateUserInput }, { models }: Context) =>
      models.User.create({ ...input }),
    
    login: (_: unknown, { input }: { input: LoginInput }, { models }: Context) =>
      doLogin(input.email, input.password, models)
  }
}

Apollo Client

import { ApolloClient, ApolloProvider, InMemoryCache, gql, useQuery, useMutation } from '@apollo/client'

const client = new ApolloClient({
  uri: 'http://localhost:4000/graphql',
  cache: new InMemoryCache()
})

const App: React.FC = () => (
  <ApolloProvider client={client}>
    <Main />
  </ApolloProvider>
)

// Types
interface User {
  id: string
  username: string
  email: string
}

interface GetUsersData {
  getUsers: User[]
}

// Query
const GET_USERS = gql`
  query GetUsers {
    getUsers {
      id
      username
      email
    }
  }
`

const { data, loading, error } = useQuery<GetUsersData>(GET_USERS)

// Mutation
interface LoginInput {
  email: string
  password: string
}

interface LoginResponse {
  login: {
    token: string
  }
}

const LOGIN = gql`
  mutation Login($email: String!, $password: String!) {
    login(input: { email: $email, password: $password }) {
      token
    }
  }
`

const [login] = useMutation<LoginResponse, LoginInput>(LOGIN)

Chapter 14: MonoRepo Architecture

NPM Workspaces Setup

// Root package.json
{
  "name": "web-creator",
  "private": true,
  "workspaces": [
    "packages/*"
  ]
}

Package Structure

root/
├── packages/
│   ├── api/
│   │   └── package.json (@web-creator/api)
│   ├── frontend/
│   │   └── package.json (@web-creator/frontend)
│   └── utils/
│       └── package.json (@web-creator/utils)
└── package.json

Shared TypeScript Config

// tsconfig.common.json
{
  "compilerOptions": {
    "target": "ESNext",
    "jsx": "react-jsx",
    "strict": true,
    "esModuleInterop": true
  }
}

// Package tsconfig.json
{
  "extends": "../../tsconfig.common.json",
  "compilerOptions": {
    "outDir": "./dist"
  }
}

Webpack Config for Packages

// webpack.common.ts
import { resolve } from 'path'

interface WebpackArgs {
  packageName: string
}

export default (args: WebpackArgs) => ({
  entry: `./src/index.ts`,
  output: {
    path: resolve(__dirname, `../../../${args.packageName}/dist`),
    filename: 'index.js'
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js'],
    alias: {
      '~': resolve(__dirname, `../../../${args.packageName}/src`)
    }
  }
})

Chapter 15: Improving Performance

Reconciliation and Keys

interface Item {
  id: string
  name: string
}

// Good - stable unique keys
{items.map((item: Item) => (
  <li key={item.id}>{item.name}</li>
))}

// Bad - using index
{items.map((item: Item, index: number) => (
  <li key={index}>{item.name}</li>
))}

React.memo

interface ExpensiveComponentProps {
  data: string
}

const ExpensiveComponent: React.FC<ExpensiveComponentProps> = memo(({ data }) => {
  // Expensive calculations
  return <div>{data}</div>
})

useMemo for Expensive Calculations

const expensiveValue = useMemo<number>(() => {
  return computeExpensiveValue(a, b)
}, [a, b])

useCallback for Functions

const memoizedCallback = useCallback(() => {
  doSomething(a, b)
}, [a, b])

Code Splitting

import { lazy, Suspense } from 'react'

const LazyComponent = lazy(() => import('./LazyComponent'))

<Suspense fallback={<Loading />}>
  <LazyComponent />
</Suspense>

Production Build

// webpack.config.js
new webpack.DefinePlugin({
  'process.env.NODE_ENV': JSON.stringify('production')
})

// Split bundles
optimization: {
  splitChunks: {
    cacheGroups: {
      vendor: {
        test: /node_modules/,
        name: 'vendor',
        chunks: 'all'
      }
    }
  }
}

Chapter 16: Testing and Debugging

Jest Setup

npm install --save-dev jest @testing-library/react @testing-library/jest-dom
// jest.config.js
module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'jsdom',
  setupFilesAfterEn

v: ['<rootDir>/setupTests.ts']
}

Basic Component Test

import { render, screen } from '@testing-library/react'

interface HelloProps {
  name?: string
}

describe('Hello Component', () => {
  it('should render Hello World', () => {
    render(<Hello />)
    expect(screen.getByText('Hello World')).toBeInTheDocument()
  })
  
  it('should render the name prop', () => {
    render(<Hello name="Carlos" />)
    expect(screen.getByText('Hello Carlos')).toBeInTheDocument()
  })
})

Testing Events

import { fireEvent, render } from '@testing-library/react'

it('should handle click', () => {
  const { container } = render(<ShowInformation />)
  const button = container.querySelector('button')
  
  if (button) {
    fireEvent.click(button)
  }
  
  expect(container.querySelector('.info')).toBeInTheDocument()
})

Vitest Setup

npm install -D vitest @vitest/ui @testing-library/react
// vite.config.ts
import { defineConfig } from 'vitest/config'

export default defineConfig({
  test: {
    environment: 'jsdom',
    globals: true
  }
})

React DevTools

  • Install Chrome extension
  • Inspect component tree
  • View props and state
  • Profile performance

Redux DevTools

import { composeWithDevTools } from 'redux-devtools-extension'

const store = createStore(
  rootReducer,
  composeWithDevTools(applyMiddleware(...middleware))
)

Chapter 17: Deploying to Production

DigitalOcean Droplet Setup

# SSH into droplet
ssh root@YOUR_DROPLET_IP

# Install Node.js
curl -sL https://deb.nodesource.com/setup_19.x -o nodesource_setup.sh
sudo bash nodesource_setup.sh
sudo apt install nodejs -y

# Install PM2
npm install -g pm2

# Install nginx
sudo apt-get update
sudo apt-get install nginx

Nginx Configuration

# /etc/nginx/sites-available/default
location / {
  proxy_pass http://localhost:3000;
  proxy_http_version 1.1;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection 'upgrade';
  proxy_set_header Host $host;
  proxy_cache_bypass $http_upgrade;
}

PM2 Process Management

# Start app
pm2 start npm --name "app" -- start

# View logs
pm2 logs

# Restart
pm2 restart app

# Stop
pm2 stop app

CircleCI Configuration

# .circleci/config.yml
version: 2.1
jobs:
  build:
    docker:
      - image: cimg/node:18.12.1
    steps:
      - checkout
      - run: npm install
      - run: npm run lint
      - run: npm test
      - run: ssh $DROPLET_USER@$DROPLET_IP 'cd app; git pull; npm install; pm2 restart app'

workflows:
  build-deploy:
    jobs:
      - build:
          filters:
            branches:
              only: master

Quick Reference

Common Hooks

Hook Purpose Example
useState Manage state const [state, setState] = useState<number>(0)
useEffect Side effects useEffect(() => { }, [deps])
useContext Context consumer const value = useContext(MyContext)
useRef DOM reference const ref = useRef<HTMLDivElement>(null)
useMemo Memoize value useMemo(() => compute(), [deps])
useCallback Memoize function useCallback(() => fn(), [deps])
useReducer Complex state const [state, dispatch] = useReducer(reducer, init)

Performance Tips

  • ✅ Use React.memo for expensive components
  • ✅ Use useMemo for expensive calculations
  • ✅ Use useCallback for functions passed as props
  • ✅ Use proper keys in lists
  • ✅ Code split with React.lazy
  • ❌ Don't optimize prematurely
  • ❌ Don't use index as key
  • ❌ Don't mutate state directly

Best Practices

  • Write small, focused components
  • Keep components pure when possible
  • Use TypeScript for type safety
  • Test components thoroughly
  • Follow ESLint rules
  • Use meaningful variable names
  • Document complex logic
  • Keep state as local as possible

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment