The TypeScript version is in the comments (recommended).
- 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.
- 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.
- Parent → Child: via props
- Child → Parent: via callback functions
- Sibling → Sibling: via lifting state up or context
- Context API – For global state management (avoiding prop drilling).
- Reducers – Pure functions for state updates.
- Redux / Zustand / Recoil / Jotai – External state management libraries.
- React Router – For navigation between pages (SPA behavior).
- Route, Link, Navigate, Outlet – Core components of routing.
- CSS Modules – Scoped CSS for components.
- Styled Components / Emotion – CSS-in-JS libraries.
- Tailwind CSS – Utility-first styling framework.
- 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.
- React.memo – Prevents re-rendering of unchanged components.
- Lazy Loading & Suspense – Code-splitting and dynamic imports.
- Profiler – Analyzes rendering performance.
- 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.
- Next.js / Remix – Frameworks built on top of React.
- Vite / CRA – React app bootstrapping tools.
- React DevTools – Browser extension for debugging.
- Imperative: Describes how things work (step-by-step instructions)
- Declarative: Describes what you want to achieve (React's approach)
// 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!' }
}
}
}- 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()
npm install -g create-vite
create-vite my-react-app --template react-ts
cd my-react-app
npm install
npm run dev// 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
}interface IUser {
username: string
email: string
age?: number
}
// Extending interfaces
interface IPerson extends IWork {
name: string
age: number
}enum Colors {
PRIMARY = '#FF0000',
SECONDARY = '#00FF00',
TERTIARY = '#0000FF'
}type Theme = 'light' | 'dark'{
"compilerOptions": {
"target": "ESNext",
"jsx": "react-jsx",
"strict": true,
"esModuleInterop": true
}
}// Props
<img src="..." alt="..." />
// Children
<Button>Click me!</Button>
// Expressions in JSX
<div>Hello, {name}!</div>
// Style objects
<div style={{ backgroundColor: 'red' }} />classNameinstead ofclasshtmlForinstead offor- camelCase for event handlers:
onClick,onChange - Self-closing tags required:
<img />
// Using &&
{isLoggedIn && <LogoutButton />}
// Ternary operator
{isLoggedIn ? <LogoutButton /> : <LoginButton />}
// Helper function
const canShowData = () => dataIsReady && (isAdmin || userHasPermissions)
{canShowData() && <SecretData />}<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>{
"extends": ["airbnb", "prettier"],
"rules": {
"semi": [2, "never"],
"max-len": ["error", { "code": 100 }]
}
}// 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))const Button = ({ children }) => (
<button className="btn">{children}</button>
)
<Button>
<img src="..." />
<span>Click me!</span>
</Button>// 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>
)const withClassName = Component => props => (
<Component {...props} className="my-class" />
)
const MyComponentWithClassName = withClassName(MyComponent)const Name = ({ children }) => children('World')
<Name>
{name => <div>Hello, {name}!</div>}
</Name>() => { ... }→ Needreturn() => ( ... )→ Implicit return() => <div>...</div>→ Implicit return
const [values, setValues] = useState({ firstName: '', lastName: '' })
const handleChange = ({ target: { name, value } }) => {
setValues({ ...values, [name]: value })
}
<input
name="firstName"
value={values.firstName}
onChange={handleChange}
/>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>const inputRef = useRef(null)
const handleClick = () => {
inputRef.current.focus()
}
<input type="text" ref={inputRef} />
<button onClick={handleClick}>Set Focus</button>const TextInputWithRef = React.forwardRef((props, ref) => (
<input ref={ref} type="text" {...props} />
))
// Usage
const inputRef = useRef()
<TextInputWithRef ref={inputRef} />const style = {
color: 'palevioletred',
backgroundColor: 'papayawhip',
fontSize: 16 // Numbers default to px
}
<button style={style}>Click me!</button>/* Button.module.css */
.button {
background-color: #ff0000;
padding: 20px;
}import styles from './Button.module.css'
<button className={styles.button}>Click me!</button>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>// BAD
const [count, setCount] = useState(props.count)
// GOOD - make it explicit
const [count, setCount] = useState(props.initialCount)// BAD
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
// GOOD
{items.map((item) => (
<li key={item.id}>{item}</li>
))}// BAD
<div {...props} />
// GOOD
<div {...props.domProps} />const [counter, setCounter] = useState(0)
const handleCounter = (operation) => {
if (operation === 'add') {
setCounter(counter + 1)
} else {
setCounter(counter - 1)
}
}// componentDidMount
useEffect(() => {
// runs once
}, [])
// componentDidUpdate
useEffect(() => {
// runs on every render
})
// with dependencies
useEffect(() => {
// runs when dependencies change
}, [dependency1, dependency2])
// cleanup
useEffect(() => {
return () => {
// cleanup code
}
}, [])const handleDelete = useCallback((taskId) => {
const newTodoList = todoList.filter(todo => todo.id !== taskId)
setTodoList(newTodoList)
}, [todoList])const filteredTodoList = useMemo(() =>
todoList.filter(todo =>
todo.task.toLowerCase().includes(term.toLowerCase())
),
[term, todoList]
)const MyComponent = memo(({ name }) => (
<div>{name}</div>
))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 })- Only call Hooks at the top level
- Only call Hooks from React functions
npm install react-router-dom @types/react-router-domimport { 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>// Define route
<Route path="/contacts/:contactId" element={<Contact />} />
// Access params
const { contactId } = useParams()import { Link } from 'react-router-dom'
<Link to="/about">About</Link>// 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>
}- Time slicing for better responsiveness
- Suspense for data fetching
- Concurrent rendering
// React 18 batches these updates automatically
setCount(count + 1)
setCount(count + 1)
setCount(count + 1)
// Results in single renderimport { useTransition } from 'react'
const [isPending, startTransition] = useTransition()
startTransition(() => {
setSearchTerm(value)
})<Suspense fallback={<Loading />}>
<UserProfile />
</Suspense>// 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 />)// 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
}, [])// 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)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>// 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())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)// 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} />
)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>
}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!
}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 })
})
)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)
}
}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)// Root package.json
{
"name": "web-creator",
"private": true,
"workspaces": [
"packages/*"
]
}root/
├── packages/
│ ├── api/
│ │ └── package.json (@web-creator/api)
│ ├── frontend/
│ │ └── package.json (@web-creator/frontend)
│ └── utils/
│ └── package.json (@web-creator/utils)
└── package.json
// tsconfig.common.json
{
"compilerOptions": {
"target": "ESNext",
"jsx": "react-jsx",
"strict": true,
"esModuleInterop": true
}
}
// Package tsconfig.json
{
"extends": "../../tsconfig.common.json",
"compilerOptions": {
"outDir": "./dist"
}
}// 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`)
}
}
})// 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>
))}const ExpensiveComponent = memo(({ data }) => {
// Expensive calculations
return <div>{data}</div>
})const expensiveValue = useMemo(() => {
return computeExpensiveValue(a, b)
}, [a, b])const memoizedCallback = useCallback(() => {
doSomething(a, b)
}, [a, b])import { lazy, Suspense } from 'react'
const LazyComponent = lazy(() => import('./LazyComponent'))
<Suspense fallback={<Loading />}>
<LazyComponent />
</Suspense>// 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'
}
}
}
}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']
}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()
})
})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()
})npm install -D vitest @vitest/ui @testing-library/reacttypescript
// vite.config.ts
export default defineConfig({
test: {
environment: 'jsdom',
globals: true
}
})- Install Chrome extension
- Inspect component tree
- View props and state
- Profile performance
import { composeWithDevTools } from 'redux-devtools-extension'
const store = createStore(
rootReducer,
composeWithDevTools(applyMiddleware(...middleware))
)# 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 nginxnginx
# /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;
}# Start app
pm2 start npm --name "app" -- start
# View logs
pm2 logs
# Restart
pm2 restart app
# Stop
pm2 stop app# .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: masterHook 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)
- ✅ Use
React.memofor expensive components - ✅ Use
useMemofor expensive calculations - ✅ Use
useCallbackfor 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
- 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
React.js Main Concepts (TypeScript)
Core Fundamentals
Hooks (Functional Component Features)
Component Communication
Context API & State Management
Routing
Styling
Component Patterns
Performance Optimization
Advanced Concepts
Ecosystem & Tools
Chapter 1: Taking Your First Steps with React
Declarative vs Imperative Programming
React Elements
Key Concepts
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 devChapter 2: Introducing TypeScript
Basic Types
Interfaces
Enums
Template Literal Types
tsconfig.json Essentials
{ "compilerOptions": { "target": "ESNext", "jsx": "react-jsx", "strict": true, "esModuleInterop": true } }Chapter 3: Cleaning Up Your Code
JSX Basics
JSX vs HTML Differences
classNameinstead ofclasshtmlForinstead offoronClick,onChange<img />Conditional Rendering
Lists and Keys
ESLint Configuration
{ "extends": ["airbnb", "prettier"], "rules": { "semi": [2, "never"], "max-len": ["error", { "code": 100 }] } }Functional Programming Concepts
Return Statement
() => { ... }→ Needreturn() => ( ... )→ Implicit return() => <div>...</div>→ Implicit returnChapter 4: Exploring Popular Composition Patterns
Children Prop
Container and Presentational Pattern
Higher-Order Components (HOCs)
Function as Child Pattern
Chapter 5: Writing Code for the Browser
Controlled Components
Event Handling
Refs
forwardRef
Chapter 6: Making Your Components Look Beautiful
Inline Styles
CSS Modules
Styled Components
Chapter 7: Anti-Patterns to Be Avoided
❌ Initializing State with Props
❌ Using Index as Key
❌ Spreading Props on DOM Elements
Chapter 8: React Hooks
useState
useEffect
useCallback
useMemo
React.memo
useReducer
Rules of Hooks
Chapter 9: React Router
Basic Setup
Route Parameters
Navigation
React Router v6.4 Loaders
Chapter 10: React 18 New Features
Concurrent Mode
Automatic Batching
Transitions
Suspense
New APIs
New Hooks
Chapter 10.1: React 19 New Features
Key Features
<title>,<meta>,<link>componentsuseActionState,useFormStatus,useOptimisticActions
useFormStatus
useOptimistic
Document Metadata
Server Components
'use server' Directive
Asset Loading
Migration Tips
Chapter 11: Managing Data
React Context API
SWR (Stale-While-Revalidate)
Redux Toolkit
Chapter 12: Server-Side Rendering
Basic SSR with Express
Data Fetching in SSR
Next.js Setup
Chapter 13: Understanding GraphQL
GraphQL Schema
Apollo Server Setup
Resolvers
Apollo Client
Chapter 14: MonoRepo Architecture
NPM Workspaces Setup
Package Structure
Shared TypeScript Config
Webpack Config for Packages
Chapter 15: Improving Performance
Reconciliation and Keys
React.memo
useMemo for Expensive Calculations
useCallback for Functions
Code Splitting
Production Build
Chapter 16: Testing and Debugging
Jest Setup
Basic Component Test
Testing Events
Vitest Setup
React DevTools
Redux DevTools
Chapter 17: Deploying to Production
DigitalOcean Droplet Setup
Nginx Configuration
PM2 Process Management
CircleCI Configuration
Quick Reference
Common Hooks
const [state, setState] = useState<number>(0)useEffect(() => { }, [deps])const value = useContext(MyContext)const ref = useRef<HTMLDivElement>(null)useMemo(() => compute(), [deps])useCallback(() => fn(), [deps])const [state, dispatch] = useReducer(reducer, init)Performance Tips
React.memofor expensive componentsuseMemofor expensive calculationsuseCallbackfor functions passed as propsReact.lazyBest Practices