Files
Compass/docs/react.md

573 lines
15 KiB
Markdown

# React, TypeScript & JavaScript — A Deep Dive
---
## Part 1: JavaScript Fundamentals
### Everything is an Object
JavaScript has one foundational truth: **everything is an object**, or behaves like one. Numbers, strings, functions,
arrays, classes — they all ultimately descend from `Object`.
```js
typeof {} // "object"
typeof [] // "object"
typeof function () {} // "function" (but instanceof Object === true)
typeof 42 // "number" (primitive, but auto-boxed to Number object)
```
Primitives (`string`, `number`, `boolean`, `null`, `undefined`, `symbol`, `bigint`) are the exception — they're not
objects — but JavaScript automatically "boxes" them into their object wrappers when you access properties on them:
```js
'hello'.toUpperCase() // JS temporarily wraps "hello" in a String object
```
---
### Functions Are Objects
Functions in JS are **first-class objects** — they can be assigned to variables, passed as arguments, and have
properties attached to them.
```js
function greet() {
return 'hello'
}
greet instanceof Function // true
greet instanceof Object // true
// Functions can have properties like any object
greet.version = '1.0'
console.log(greet.version) // "1.0"
```
This is why you can pass functions as callbacks, store them in arrays, and return them from other functions. It's also
why React can accept both function components and class components — they're both just callable objects.
---
### Classes Are Just Functions
`class` syntax in JavaScript is **syntactic sugar** over constructor functions and prototypes. Under the hood, they
compile down to the same thing.
```js
// Modern class syntax:
class Dog {
constructor(name) {
this.name = name
}
bark() {
console.log('woof')
}
}
// Is identical to this older style:
function Dog(name) {
this.name = name
}
Dog.prototype.bark = function () {
console.log('woof')
}
// Proof:
typeof Dog // "function" — classes ARE functions
```
The `prototype` chain is how JavaScript does inheritance — when you call `dog.bark()`, JS looks up the chain: instance →
`Dog.prototype``Object.prototype`.
---
### `this` — The Most Confusing Part of JS
`this` refers to the object that **called** the function, not where the function was defined. This causes bugs when
passing methods as callbacks.
```js
class Timer {
constructor() {
this.count = 0
}
tick() {
this.count++
} // `this` works fine here
}
const t = new Timer()
setTimeout(t.tick, 1000) // ❌ `this` is undefined — tick lost its context
setTimeout(() => t.tick(), 1000) // ✅ arrow function preserves `this`
```
**Arrow functions** don't have their own `this` — they inherit it from the surrounding scope. This is why they're
preferred for callbacks and class methods in React.
---
### The Event Loop
JavaScript is **single-threaded** — it can only do one thing at a time. The event loop is how it handles async
operations without blocking:
```
Call Stack → runs sync code
Web APIs → handles async (setTimeout, fetch, etc.)
Callback Queue → waits for stack to be empty
Event Loop → moves callbacks back to stack when ready
```
```js
console.log('1')
setTimeout(() => console.log('2'), 0) // async — goes to queue
console.log('3')
// Output: 1, 3, 2
// Even with 0ms delay, setTimeout is async and waits for stack to clear
```
---
### Async / Await and Promises
`async/await` is syntactic sugar over Promises. A `Promise` represents a value that will be available in the future.
```js
// Promise style:
fetch('/api/user')
.then((res) => res.json())
.then((data) => console.log(data))
.catch((err) => console.error(err))
// Async/await style (same thing, cleaner):
async function getUser() {
try {
const res = await fetch('/api/user')
const data = await res.json()
console.log(data)
} catch (err) {
console.error(err)
}
}
```
`async` functions always return a Promise, even if you return a plain value.
---
### Arrow Functions vs Function Declarations
```js
// Function declaration — hoisted, has own `this`
function getUser() {}
getUser() // ✅ callable before definition
// Arrow function — not hoisted, inherits `this`
const getUser = () => {}
getUser() // ❌ ReferenceError if called before declaration
```
For React components and utility functions the two are functionally equivalent — the choice is mostly style. Arrow
functions are preferred for callbacks; function declarations are conventional for top-level utilities and Next.js
exports.
---
## Part 2: TypeScript
TypeScript is JavaScript with a **type system layered on top**. It compiles down to plain JavaScript — the browser never
sees TypeScript.
---
### What TypeScript Adds
```ts
// JavaScript — no safety
function add(a, b) {
return a + b
}
add('hello', 42) // runs, returns "hello42" — probably a bug
// TypeScript — caught at compile time
function add(a: number, b: number): number {
return a + b
}
add('hello', 42) // ❌ Error: Argument of type 'string' is not assignable to 'number'
```
TypeScript catches bugs before runtime, makes refactoring safer, and provides autocomplete in editors.
---
### `interface` vs `type`
Both describe the shape of an object, but they have key differences:
```ts
// interface — extendable, mergeable, best for object shapes
interface User {
name: string
age: number
}
interface Admin extends User {
role: string
}
// Declaration merging — only interfaces can do this
interface User {
email: string
}
// User now has: name, age, email ✅
// type — more flexible, supports unions, tuples, primitives
type ID = string | number // union — impossible with interface
type Pair = [string, number] // tuple — impossible with interface
type User = {name: string} // object shape — works like interface
// type uses intersection instead of extends:
type Admin = User & {role: string}
// type CANNOT be merged:
type User = {email: string} // ❌ Duplicate identifier error
```
**Rule of thumb:** Use `interface` for component props and object shapes; use `type` for unions, tuples, or complex
compositions.
---
### Generics
Generics let you write reusable code that works with multiple types while remaining type-safe:
```ts
// Without generics — loses type info
function first(arr: any[]): any {
return arr[0]
}
// With generics — type is preserved
function first<T>(arr: T[]): T {
return arr[0]
}
const num = first([1, 2, 3]) // TypeScript knows: num is number
const str = first(['a', 'b']) // TypeScript knows: str is string
```
---
### Utility Types
TypeScript ships with built-in type transformers:
```ts
interface User {
name: string
age: number
email: string
}
Partial<User> // all fields optional
Required<User> // all fields required
Pick<User, 'name'> // only { name: string }
Omit<User, 'email'> // everything except email
Readonly<User> // all fields immutable
Record<string, User> // { [key: string]: User }
```
These are heavily used in React for prop types — e.g., `Partial<ButtonProps>` for a component where all props are
optional.
---
## Part 3: React
React is a **UI library** built on one core idea: your UI is a function of your state.
```
UI = f(state)
```
When state changes, React re-runs your component function and figures out the minimal DOM updates needed.
---
### What React Actually Does
React never touches the real DOM directly. It maintains a **Virtual DOM** — a lightweight JavaScript object tree
representing the UI. When state changes:
1. React re-renders the component (calls the function again)
2. Compares new Virtual DOM to previous (called **diffing** or **reconciliation**)
3. Applies only the minimal changes to the real DOM (**patching**)
This is fast because DOM operations are expensive; JS object comparisons are cheap.
---
### JSX
JSX looks like HTML in JavaScript but it's neither — it's **syntactic sugar** for `React.createElement()` calls:
```jsx
// What you write:
const el = <div className="box">Hello</div>
// What it compiles to:
const el = React.createElement("div", {className: "box"}, "Hello")
// Which produces a plain JS object:
{
type: "div",
props
:
{
className: "box",
children
:
"Hello"
}
}
```
React reads these objects to build and update the DOM.
---
### Components — The Building Block
A component is any function that returns JSX. React identifies components by their capital letter.
```tsx
// Function component (modern standard)
function Button({label, onClick}: {label: string; onClick: () => void}) {
return <button onClick={onClick}>{label}</button>
}
// Class component (legacy)
class Button extends React.Component<{label: string}> {
render() {
return <button>{this.props.label}</button>
}
}
```
**Why both work:** React calls `Component(props)` for function components and `new Component(props).render()` for class
components. Both return JSX objects, which is all React needs. Since JavaScript classes are just functions with
prototypes, they're both "callable things that return JSX" at the engine level.
---
### The Component Lifecycle
Every component goes through: **mount → update → unmount**
```
Mount → component appears in the DOM
Update → state or props change, component re-renders
Unmount → component is removed from the DOM
```
In function components, `useEffect` handles all lifecycle stages:
```tsx
useEffect(() => {
// Runs after mount (and after updates if deps change)
const subscription = subscribe()
return () => {
// Cleanup — runs before unmount (or before next effect)
subscription.unsubscribe()
}
}, [dependency]) // runs again whenever `dependency` changes
// [] = run once on mount only
// no array = run after every render
```
---
### Props vs State
```tsx
// Props — data passed IN from a parent. Read-only.
function Greeting({name}: {name: string}) {
return <h1>Hello, {name}</h1>
}
// State — data owned BY the component. Triggers re-render when changed.
function Counter() {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(count + 1)}>{count}</button>
}
```
The key distinction: **props flow down** (parent → child), **state lives inside** the component.
---
### Hooks
Hooks are a **special category of function** — not a component, not a plain method. They let function components access
React features like state and lifecycle. They have strict rules: only call them at the top level of a component (not
inside conditions or loops), and only inside React functions.
```tsx
useState() // local component state
useEffect() // side effects & lifecycle
useRef() // reference a DOM element or persist a value without re-render
useContext() // consume a React Context
useMemo() // memoize an expensive calculation
useCallback() // memoize a function reference
useReducer() // complex state logic (like a mini Redux)
```
**Custom hooks** let you extract and reuse stateful logic:
```tsx
function useWindowWidth() {
const [width, setWidth] = useState(window.innerWidth)
useEffect(() => {
const handler = () => setWidth(window.innerWidth)
window.addEventListener('resize', handler)
return () => window.removeEventListener('resize', handler)
}, [])
return width
}
// Reusable in any component:
function Layout() {
const width = useWindowWidth()
return <div>{width > 768 ? <Sidebar /> : null}</div>
}
```
---
### Context — Avoiding Prop Drilling
Passing props through many layers of components is called **prop drilling**. Context solves it by broadcasting data to
any component in the tree.
```tsx
// Create context
const ThemeContext = createContext<'light' | 'dark'>('light')
// Provide it high in the tree
function App() {
return (
<ThemeContext.Provider value="dark">
<Layout />
</ThemeContext.Provider>
)
}
// Consume it anywhere below — no props needed
function Button() {
const theme = useContext(ThemeContext)
return <button className={theme}>Click</button>
}
```
---
### React's Different "Types" of Things
| Type | Returns | Naming | Purpose |
| -------------------- | ---------------- | --------------- | ----------------------------- |
| **Component** | JSX | Capitalized | UI building block |
| **Hook** | Data / functions | `use` prefix | Stateful logic |
| **Event Handler** | void / anything | `handle` prefix | Responds to events |
| **Utility Function** | Anything | Anything | Pure logic, no React rules |
| **HOC** | A component | `with` prefix | Wraps and enhances components |
| **Custom Hook** | Data / functions | `use` prefix | Reusable stateful logic |
| **Context** | — | — | Cross-tree data sharing |
| **Ref** | Mutable object | — | DOM access / stable values |
| **Portal** | — | — | Render outside DOM tree |
---
### A Complete Component in Context
```tsx
// Types
interface ProfileProps {
profile: Profile
label: string // a prop, not a second parameter
}
// Component
export function ProfilePersonality({profile, label}: ProfileProps) {
// Hook — stateful logic
const [isExpanded, setIsExpanded] = useState(false)
// Derived value — plain calculation, not a hook
const mbtiType = label + '-MBTI'
// Effect — side effect (lifecycle)
useEffect(() => {
document.title = `Profile: ${profile.name}`
return () => {
document.title = 'App'
}
}, [profile.name])
// Event handler
const handleToggle = () => setIsExpanded((prev) => !prev)
// JSX — the UI
return (
<div>
{mbtiType && <span>{mbtiType}</span>}
<button onClick={handleToggle}>{isExpanded ? 'Show less' : 'Show more'}</button>
</div>
)
}
```
---
## The Big Picture
```
JavaScript
├── Everything is an object (or behaves like one)
├── Functions are first-class objects
├── Classes are functions with prototype chains
└── Async model: single-threaded + event loop
↓ adds
TypeScript
├── Static type checking at compile time
├── interfaces & types for describing shapes
├── Generics for reusable type-safe code
└── Compiles away — browser sees plain JS
↓ builds
React
├── UI = f(state) — components are functions of their data
├── Virtual DOM + diffing = efficient real DOM updates
├── JSX compiles to React.createElement() calls
├── Hooks give functions lifecycle + state (no class needed)
└── Props flow down, state lives inside, context broadcasts
```
The layering is intentional: TypeScript makes JavaScript safer; React gives JavaScript a model for building reactive
UIs. Understanding JS fundamentals (objects, functions, `this`, async) makes React's behaviour — re-renders, closures in
hooks, stale state bugs — much more predictable.