└── README.md /README.md: -------------------------------------------------------------------------------- 1 | # Vanilla JavaScript for React Developers 2 | 3 | This book will guide you through re-implementing common React patterns and features using nothing but **Vanilla JS**, helping you deepen your understanding of the web platform and become a more versatile developer. 4 | 5 | *Note: This book was formatted using an LLM* 6 | 7 | ## Introduction 8 | 9 | If you're comfortable building apps with React and its ecosystem but feel less confident working with raw JavaScript and browser APIs, this book is for you. We assume you know the basics of web development (HTML/CSS/JS) and have built React apps, but you might not have created complex apps without a framework. We'll bridge that gap by showing how React's abstractions map to plain JavaScript techniques. 10 | 11 | You'll learn how to create dynamic interfaces, manage state, compose components, handle routing, and perform side effects using **just the platform** (no frameworks). We'll review JavaScript fundamentals (like closures and the event loop) to firm up your understanding. Then, we'll dive into building real projects – from a simple to-do list to more advanced apps like a movie explorer and beyond – all in Vanilla JS. Along the way, we'll emphasize a **no-build toolchain** where possible (just use a browser and editor), and then show how to prepare for production with bundlers (using Vite) and even a touch of Node.js for backend concepts. 12 | 13 | Each chapter addresses a specific aspect of app development. Chapters include explanations, code examples, and comparisons to how you might do the same in React. We've included several hands-on tutorials/projects to practice the concepts. You can type the code yourself, or if you have an AI coding assistant, you'll find optional prompts to help generate or validate solutions. *(For example, after reading a project chapter, you might ask your AI assistant to "build a to-do app in vanilla JavaScript" to compare with your code.)* Use these AI prompts as a learning tool – but we encourage you to work through the code manually first for maximum learning. 14 | 15 | Let's get started on our journey from React to **real** Reactivity – the kind that doesn't require a library at all! 16 | 17 | ## Chapter 1. JavaScript Fundamentals for React Developers 18 | 19 | Before we build anything, let's review some core JavaScript concepts. As a React developer, you've certainly used JavaScript daily, but some fundamentals might have been abstracted away by the framework. Understanding these will make it easier to implement React-like patterns in plain JS. 20 | 21 | ### 1.1 Closures and Scope 22 | 23 | JavaScript functions are **first-class citizens** and form closures. A **closure** is created whenever a function retains access to its lexical scope even after that outer function has finished executing. In practical terms, this means an inner function can remember and use variables from an outer function even after the outer function returns. For example: 24 | 25 | ```js 26 | function createCounter() { 27 | let count = 0; 28 | return function increment() { 29 | count++; 30 | console.log(count); 31 | }; 32 | } 33 | 34 | const counter = createCounter(); 35 | counter(); // 1 36 | counter(); // 2 37 | ``` 38 | 39 | Here, `createCounter` returns an `increment` function. The returned function "closes over" the `count` variable. Each time you call `counter()`, it remembers the last value of `count` because of the closure. Closures are useful for **encapsulating state and creating private variables** in Vanilla JS, similar to how React components might hold state via hooks. In fact, hooks like `useState` leverage closures under the hood to retain state between renders. 40 | 41 | ### 1.2 The Event Loop and Async JavaScript 42 | 43 | In React, you often use effects or callbacks that rely on JavaScript's asynchronous nature (like fetching data or debouncing inputs). Understanding the JavaScript **event loop** will clarify how tasks are handled without blocking the UI. 44 | 45 | JavaScript has a **single-threaded** concurrency model based on an event loop. The runtime maintains a **call stack** (for function execution) and a **message queue** (for events and async callbacks). As an example: 46 | 47 | - When you call functions, each call is pushed onto the stack, and popped off when it returns. 48 | - Asynchronous tasks (like `setTimeout` callbacks, promises, or DOM events) are placed in a queue (or *microtask* queue for promises) to be processed later. 49 | - The event loop continuously checks the queue and moves ready tasks to the stack **when the stack is free** ([Concurrency model and Event Loop - JavaScript | MDN](https://lia.disi.unibo.it/materiale/JS/developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop.html#:~:text=Queue)). In other words, *"when the stack is empty, a message (task) is taken out of the queue and processed by calling its associated function, until the stack is empty again"* ([Concurrency model and Event Loop - JavaScript | MDN](https://lia.disi.unibo.it/materiale/JS/developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop.html#:~:text=Queue)). 50 | 51 | This design ensures the UI isn't blocked while waiting for long operations – the browser can handle other work (like rendering or user input) while JavaScript awaits events. For instance, when you do a `fetch().then(...)` in Vanilla JS, the `.then` callback goes to the queue and runs only after the current call stack is clear. Knowing this helps you avoid pitfalls like updating DOM outside of an event or race conditions with state. 52 | 53 | ### 1.3 Prototypes and Objects 54 | 55 | React uses class components (less nowadays) and plain objects for props/state. In Vanilla JS, understanding prototypes is key for working with objects and even for creating classes. 56 | 57 | **Prototypal Inheritance:** JavaScript uses prototypes for inheritance. Every object has an internal link to a prototype object, from which it can inherit properties. As MDN notes, *"Prototypes are the mechanism by which JavaScript objects inherit features from one another."* ([Object prototypes - Learn web development | MDN](https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Object_prototypes#:~:text=Prototypes%20are%20the%20mechanism%20by,an%20object%20can%20be%20set)). When you access a property on an object, the JS engine will first look at the object itself, and if not found, it will climb up the prototype chain to see if the property exists on the prototype, then the prototype's prototype, and so on. 58 | 59 | For example, if you create an array `let arr = [1,2,3]`, `arr` automatically inherits methods like `push` and `pop` from `Array.prototype`. This is why `arr.push(4)` works even though you didn't define `push` on `arr` – it's inherited. 60 | 61 | You can leverage prototypes directly (using `Object.create` or setting `MyConstructor.prototype`) or use the newer class syntax which under the hood uses prototypes. For instance: 62 | 63 | ```js 64 | class Car { 65 | constructor(model) { 66 | this.model = model; 67 | } 68 | drive() { 69 | console.log(`${this.model} is driving`); 70 | } 71 | } 72 | const c = new Car('Tesla'); 73 | c.drive(); // "Tesla is driving" 74 | ``` 75 | 76 | Here `Car.prototype.drive` is what provides the `drive` method to instances. Understanding this will help when we create multiple components or want to structure our code with classes in later chapters. It's also useful when reading framework code – many libraries use prototypal patterns under the hood. 77 | 78 | ### 1.4 The `this` Keyword 79 | 80 | In React's functional components, you may not deal with `this` much (aside from maybe in class components or event handlers). But in Vanilla JS, especially when working with DOM events or object methods, `this` is still important. It refers to the context object on which a function is invoked. For example: 81 | 82 | ```js 83 | const player = { 84 | name: 'Alice', 85 | greet() { 86 | console.log(`Hello, ${this.name}`); 87 | } 88 | }; 89 | player.greet(); // "Hello, Alice" 90 | ``` 91 | 92 | In `player.greet()`, `this` refers to `player`. However, if you detach the method, e.g. `const hello = player.greet; hello();`, `this` would be `undefined` (or the global object in non-strict mode) because it's called without an owner. We'll see this in DOM event handlers: `this` inside a handler refers to the element that received the event. We'll mostly use modern arrow functions and `bind` where necessary to control `this`, but keep this behavior in mind. 93 | 94 | ### 1.5 Summary 95 | 96 | These fundamentals – **closures**, **event loop**, **prototypes**, and **this** – form the foundation for the patterns we'll implement. If they feel abstract now, don't worry. As we build actual projects, we'll reinforce these concepts in context. Feel free to refer back to this chapter as needed. Next, we'll start building the pieces of a user interface with Vanilla JS, much like how React composes a UI from components. 97 | 98 | *(You've completed the foundational crash course! If you want a quick refresher via an AI assistant, you could ask: "Explain JavaScript closures, the event loop, and prototypes in simple terms with examples." But otherwise, onward to building UIs without React!)* 99 | 100 | ## Chapter 2. Building and Updating the DOM: Views Without React 101 | 102 | One of React's core strengths is abstracting the DOM and handling updates efficiently via a virtual DOM. In Vanilla JS, **you are the framework** – you'll create, update, and remove DOM elements directly. This chapter will show you how to build a dynamic UI from scratch and keep it updated when state changes, mimicking what React does with components and JSX. 103 | 104 | ### 2.1 Creating DOM Elements and Rendering Content 105 | 106 | In React, you'd write JSX like `` and it magically appears. In Vanilla JS, you explicitly create a DOM node and attach it. There are two main ways: 107 | 108 | - **Using HTML strings** and `innerHTML`/`insertAdjacentHTML`: e.g. `container.innerHTML = ""`. 109 | - **Using DOM APIs**: e.g. `document.createElement('button')` and then setting properties. 110 | 111 | Let's try the DOM API approach, as it's safer (avoids parsing HTML that could include scripts, etc.): 112 | 113 | ```js 114 | // Vanilla JS render example 115 | const appRoot = document.getElementById('app'); // assume
in HTML 116 | const helloBtn = document.createElement('button'); 117 | helloBtn.textContent = 'Hello'; // equivalent to setting innerText 118 | appRoot.appendChild(helloBtn); 119 | ``` 120 | 121 | Three lines replaced what JSX would do in one, but it's straightforward: we created a `