From 595f6158cdb98393392ac6ed64a330d2f35e90b5 Mon Sep 17 00:00:00 2001 From: dtauer Date: Mon, 28 Oct 2024 14:46:28 -0500 Subject: [PATCH] spelling, links, etc --- lessons/01-welcome/A-intro.md | 13 +++++- lessons/01-welcome/B-my-setup.md | 3 +- .../A-react-without-a-build-step.md | 40 +++++++++---------- lessons/02-no-frills-react/B-components.md | 7 ++-- lessons/03-tools/A-npm.md | 7 +++- lessons/03-tools/B-code-formatting.md | 3 ++ lessons/03-tools/C-linting.md | 15 ++++--- lessons/03-tools/D-git.md | 5 ++- lessons/03-tools/E-vite.md | 7 +++- lessons/04-core-react-concepts/A-jsx.md | 22 +++++++--- lessons/04-core-react-concepts/B-hooks.md | 10 +++-- lessons/04-core-react-concepts/C-effects.md | 7 ++-- lessons/04-core-react-concepts/D-dev-tools.md | 4 +- .../04-core-react-concepts/E-custom-hooks.md | 7 +++- .../F-handling-user-inputs.md | 7 +++- lessons/04-core-react-concepts/G-context.md | 13 ++++-- lessons/05-ecosystem/A-tanstack-router.md | 15 ++++--- lessons/05-ecosystem/B-tanstack-query.md | 7 +++- lessons/06-advanced-react/A-portals.md | 7 ++++ .../06-advanced-react/B-error-boundaries.md | 5 ++- .../06-advanced-react/C-uncontrolled-forms.md | 3 ++ lessons/07-testing/B-basic-react-tests.md | 2 +- lessons/07-testing/F-coverage.md | 3 -- lessons/08-whats-next/A-react-19.md | 9 +++-- lessons/08-whats-next/B-form-actions.md | 5 ++- lessons/08-whats-next/C-use-and-suspense.md | 2 + lessons/08-whats-next/D-react-compiler.md | 3 ++ lessons/09-wrap-up/A-congrats.md | 4 +- package-lock.json | 2 +- 29 files changed, 160 insertions(+), 77 deletions(-) diff --git a/lessons/01-welcome/A-intro.md b/lessons/01-welcome/A-intro.md index 30cef36..00f11a2 100644 --- a/lessons/01-welcome/A-intro.md +++ b/lessons/01-welcome/A-intro.md @@ -13,6 +13,7 @@ keywords: - Vite - JSX --- + > 🚨 You do not need to watch/read previous versions of this course. This is just the ninth revision of the course. Hello! And welcome to the Complete Intro to React, version 9. In this course you will go from not knowing anything about React to being job ready. This course teaches all the core concepts of React aiming to be the most useful. You will learn React from a first-principles methodology – we will start with no build tools or anything like that, just vanilla JavaScript and React. Over time we add tools like Vite, JSX, ESLint, Prettier, etc. so you can learn how to construct your own stack from scratch. I will teach a few ecosystem tools as well, testing, and what's coming soon for React. @@ -48,11 +49,17 @@ I write these courses and take care to not make mistakes. However when teaching ## How the repo works -There are two repos for this class: [the website you're currently on][site] and [the example projects][projects]. +There are two repos for this class: [the website you're currently on][site] and [the example projects][projects]. To get set up, clone or [download][zip] the projects repo: + +```bash +git clone https://github.com/btholt/citr-v9-project.git +``` Every step of this project will have a folder that will be a snapshot of where the project is at that step. If you get stuck, want to copy/paste some long bit of code you don't feel like writing, or just want to walk through the code at that point, please do! The primary goal of this is for you to learn so as long as you're learning there's no cheating! -The naming format will be `XX-` so you can get a rough idea of order and which lesson the step is coming from. In each snapshot you'll have to run `npm install` again since it'll literally just be another whole copy of the project. +The naming format will be `XX-` so you can get a rough idea of order and which lesson the step is coming from. In each snapshot you'll have to run `npm install` again since they are another whole copy of the project. + +We're going to be starting from scratch, but you'll need the repo downloaded because there's an `api` directory that will be used later in the course. > And one last request! [Please star this repo][site]. It helps the course be more discoverable and with my fragile ego. @@ -63,3 +70,5 @@ The naming format will be `XX-` so you can get a rough idea [site]: https://github.com/btholt/complete-intro-to-react-v9 [projects]: https://github.com/btholt/citr-v9-project [issues]: https://github.com/btholt/complete-intro-to-react-v9/issues +[neon]: https://neon.tech/ +[zip]: https://github.com/btholt/citr-v9-project/archive/refs/heads/main.zip \ No newline at end of file diff --git a/lessons/01-welcome/B-my-setup.md b/lessons/01-welcome/B-my-setup.md index 2218b59..638b137 100644 --- a/lessons/01-welcome/B-my-setup.md +++ b/lessons/01-welcome/B-my-setup.md @@ -13,9 +13,10 @@ keywords: - Visual Studio Code - Starship Prompt --- + ## Node.js -You'll need to have a Node.js version installed, and preferably something after v20.6. I wrote this course with 20.16 but it should be fairly future-proof. +You'll need to have a Node.js version installed, and preferably something after v20.16. I wrote this course with 20.16 but it should be fairly future-proof. I use [fnm][fnm] to manage my Node.js versions (similar to nvm). diff --git a/lessons/02-no-frills-react/A-react-without-a-build-step.md b/lessons/02-no-frills-react/A-react-without-a-build-step.md index d950d27..ec4eeed 100644 --- a/lessons/02-no-frills-react/A-react-without-a-build-step.md +++ b/lessons/02-no-frills-react/A-react-without-a-build-step.md @@ -17,31 +17,29 @@ keywords: Let's start by writing pure React. No compile step. No JSX. No Babel. No Webpack or Vite. Just some JavaScript on a page. -Let's start your project. Create your project directory. I'm going to call mine `pizza` since we're going to be building a pizza ordering system throughout this course. Create an index.html and put it into a `src/` directory inside of your project folder. In index.html put: +Let's start your project. Create your project directory inside the repo. I'm going to call mine `pizza` since we're going to be building a pizza ordering system throughout this course. Open that directory in VS Code or your editor of choice. Create an index.html and add this markup: -```javascript +```html - - - - - - - Adopt Me - - - -
not rendered
- - - - - + + + + + + Padre Gino's + + + +
not rendered
+ + + + ``` -Let's run this. We could open it directly in our browser but I like using [serve][serve]. Run `npx serve` and open localhost:3000 in your browser. +Let's run this. We could open it directly in our browser but I like using [serve][serve]. Run `npx serve` and open [http://localhost:3000/]() in your browser. - Pretty standard HTML5 document. If this is confusing, I teach another course called [Intro to Web Dev][webdev] that can help you out. - We're adding a root div. We'll render our React app here in a sec. It doesn't _have_ to be called root, just a common practice. @@ -52,7 +50,7 @@ Let's run this. We could open it directly in our browser but I like using [serve > Let's add some style! [Click here][style] to get the stylesheet for this course. Make a file called style.css in src/ and paste the previous file there. If you follow along with the course and use the same class names, the styles will be applied for you automatically. This isn't a course on CSS so I make no assertion it's any good! -Make a new directory calle src and a new file called App.js in that directory and put this in there. +Make a new directory called `src` and a new file called `App.js` in that directory and put this in there. ```javascript const App = () => { @@ -83,5 +81,5 @@ This is about the simplest React app you can build. > ReactDOM.createRoot is a new API as of React v18. The old `ReactDOM.render` is still available (and deprecated) but it'll render your app in "legacy" mode which won't use all the fun new features packed into React v18 [webdev]: https://frontendmasters.com/courses/web-development-v3/ -[style]: https://raw.githubusercontent.com/btholt/citr-v9-project/master/01-no-frills-react/src/style.css +[style]: https://github.com/btholt/citr-v9-project/blob/main/api/public/style.css [serve]: https://github.com/vercel/serve diff --git a/lessons/02-no-frills-react/B-components.md b/lessons/02-no-frills-react/B-components.md index fbb739f..b1a4fd3 100644 --- a/lessons/02-no-frills-react/B-components.md +++ b/lessons/02-no-frills-react/B-components.md @@ -13,9 +13,10 @@ keywords: - reusable components - dynamic apps --- -Now that we've done that, let's separate this out from a script tag on the DOM to its own script file (best practice.) Make a new file in your `src` directory called `App.js` and cut and paste your code into it. -Modify your code so it looks like: +Now that we've done that, let's separate this out from a script tag on the DOM to its own script file (best practice.) + +Modify your code in `App.js` so it looks like: ```javascript const Pizza = () => { @@ -42,7 +43,7 @@ root.render(React.createElement(App)); > 🚨 You will be seeing a console warning `Warning: Each child in a list should have a unique "key" prop.` in your browser console. React's dev warnings are trying to help your code run faster. Basically, React tries to keep track of components that are swapped in order. In a list, it does that by you giving it a unique key it can track. If it sees two things have swapped, it'll just move the components instead of re-rendering. - To make an element have multiple children, just pass it an array of elements. -- We created a second new component, the `Pizza` component. This component represents one pet. When you have distinct ideas represented as markup, that's a good idea to separate that it into a component like we did here. +- We created a second new component, the `Pizza` component. This component represents one pizza. When you have distinct ideas represented as markup, that's a good idea to separate that it into a component like we did here. - Since we have a new `Pizza` component, we can use it multiple times! We just use multiple calls to `React.createElement`. - In `createElement`, the last two parameters are optional. Since Pizza has no props or children (it could, we just didn't make it use them yet) we can just leave them off. diff --git a/lessons/03-tools/A-npm.md b/lessons/03-tools/A-npm.md index fba0efe..b0e19ee 100644 --- a/lessons/03-tools/A-npm.md +++ b/lessons/03-tools/A-npm.md @@ -14,12 +14,15 @@ keywords: - JavaScript tools --- -npm does not stand for Node.js Package Manager. It is, however, the package manager for Node.js. (They don't say what it stands for.) It also has all the packages in the front end scene. npm makes a command line tool, called `npm` as well. `npm` allows you to bring in code from the npm registry which is a bunch of open source modules that people have written so you can use them in your project. Whenever you run `npm install react` (don't do this yet), it will install the latest version of React from the registry. +## npm -In order to start an npm project, run `npm init -y` at the root of your project. If you don't have Node.js installed, please go install that too. When you run `npm init` it'll ask you a bunch of questions. If you don't know the answer or don't care, just hit enter. You can always modify package.json later. This will allow us to get started installing and saving packages. +npm does not stand for Node.js Package Manager. It is, however, the package manager for Node.js (they don't say what it stands for). It also has all the packages in the front end scene. npm makes a command line tool, called `npm` as well. `npm` allows you to bring in code from the npm registry which is a bunch of open source modules that people have written so you can use them in your project. Whenever you run `npm install react` (don't do this yet), it will install the latest version of React from the registry. + +In order to start an npm project, run `npm init -y` at the root of your project. If you don't have Node.js installed, [please go install that too][node]. When you run `npm init` it'll ask you a bunch of questions. If you don't know the answer or don't care, just hit enter. You can always modify package.json later. This will allow us to get started installing and saving packages. ## pnpm Another option here is to use [pnpm][pnpm]. pnpm is a newer package manager that makes different tradeoffs than npm, notably how it chooses to organize the node_modules directory. npm maintains a flat file structure and installs everything flat whereas pnpm does more symlinking. Both are fine, feel free to choose what works for you. I'll be using npm because it's easier for students to use. [pnpm]: https://pnpm.io/motivation +[node]: https://nodejs.org/en diff --git a/lessons/03-tools/B-code-formatting.md b/lessons/03-tools/B-code-formatting.md index 60aaf74..304d5fc 100644 --- a/lessons/03-tools/B-code-formatting.md +++ b/lessons/03-tools/B-code-formatting.md @@ -14,6 +14,9 @@ keywords: - Visual Studio Code - npm scripts --- + +## Prettier + It's important to keep quality high when writing code. Or at least that's how I sell ESLint and Prettier to my co-workers. In reality I'm super lazy and want the machine to do as much work as possible so I can focus more on architecture and problem-solving and less on syntax and style. While there are many tools that can help you keep code quality high, these two I consider core to my workflow. [Prettier][prettier] is an amazing tool from the brain of [James Long][jlongster]. James, like many of us, was sick of having to constantly worry about the style of his code: where to stick indents, how many, when to break lines, etc etc. Coming from languages like Go, Reason, or Elm where all that is just taken care of by the tooling for the language, this quickly wears. James did something about it and made a tool to take care of it: Prettier. diff --git a/lessons/03-tools/C-linting.md b/lessons/03-tools/C-linting.md index f08b9b5..836782e 100644 --- a/lessons/03-tools/C-linting.md +++ b/lessons/03-tools/C-linting.md @@ -13,15 +13,17 @@ keywords: - Brian Holt --- +## ESLint + On top of Prettier which takes of all the formatting, you may want to enforce some code styles which pertain more to usage: for example you may want to force people to never use `with` which is valid JS but ill advised to use. [ESLint][eslint] comes into play here. It will lint for this problems. -First of all, run `npm install -D eslint@9.9.1 eslint-config-prettier@9.1.0 globals@15.9.0` to install eslint in your project development dependencies. Then you may configure its functionalities. +First of all, run `npm install -D eslint@9.9.1 eslint-config-prettier@9.1.0 globals@15.9.0` to install ESLint in your project development dependencies. Then you may configure it. There are dozens of preset configs for ESLint and you're welcome to use any one of them. The [Airbnb config][airbnb] is very popular, as is the standard config (both of which I taught in previous versions of this class). I'm going to use a looser one for this class: the recommended JS config from ESLint. Let's create an `eslint.config.mjs` file to start linting our project. -> We're using .mjs (module JS) because we want to use import/export for modules instead of require/ +> We're using .mjs (module JS) because we want to use import/export for modules instead of require -Create this file called `eslint.config.mjs`. +Add this to the `eslint.config.mjs` file: ```js import js from "@eslint/js"; @@ -46,9 +48,9 @@ export default [ ]; ``` -- ESLint changed a lot with version 9. In previous versions of this course we did used the JSON version of configuration and that's no longer supported; you _have_ to do their newer "flat" version of config (honestly it is better.) -- The `/** @type {import('eslint').Linter.Config[]} */` is a VS Code / TypeScript trick to be able to do autocompletions on the config object. Super helpful to have the types available right in VS Code. It's not required. -- [globals][globals] is a package that is just a big JSON file of what's available in each environment. We're going to be in Node.js and Browser environments so we grabbed those two. If I was being a bit more discerning I'd carefully only apply browser configs to browser files and node configs to Node.js files. +- ESLint changed a lot with version 9. In previous versions of this course we used the JSON version of configuration which is no longer supported. You _have_ to do their newer "flat" version of config (honestly it is better.) +- The `/** @type {import('eslint').Linter.Config[]} */` is a VS Code / TypeScript trick to be able to do auto-completions on the config object. Super helpful to have the types available right in VS Code. It's not required. +- [globals][globals] is a package that is just a big JSON file of what's available in each environment. We're going to be in Node.js and Browser environments so we grabbed those two. If I was being a bit more discerning I'd carefully only apply browser configs to browser files and Node configs to Node.js files. - The config objects are applied in order. We did ESLint's JS config first, and then our custom one so we can overwrite it where we want to, and then the Prettier one should always come last as all it does is turn off rules that Prettier itself does; it doesn't add anything. This is a combination of the recommended configs of ESLint and Prettier. This will lint for both normal JS stuff as well as JSX stuff. Run `npx eslint` now and you should see we have a few errors. Run it again with the `--fix` flag and see it will fix some of it for us! Go fix the rest of your errors and come back. Let's go add this to our npm scripts. @@ -77,3 +79,4 @@ Two projects to watch going forward here: [Biome][biome] (formerly called Rome) [vscode]: https://code.visualstudio.com/ [biome]: https://biomejs.dev/ [oxlint]: https://oxc.rs/docs/guide/usage/linter.html +[globals]: https://www.npmjs.com/package/globals diff --git a/lessons/03-tools/D-git.md b/lessons/03-tools/D-git.md index e291dd0..87ca549 100644 --- a/lessons/03-tools/D-git.md +++ b/lessons/03-tools/D-git.md @@ -13,7 +13,9 @@ keywords: - frontend development --- -Git is a critical part of any project and probably something many of you are already familiar with. If you haven't, be sure to initialize your project as a git repo with `git init` in the root of your project (VSCode and any other number of tools can do this as well.) +## Git + +Git is a critical part of any project and probably something many of you are already familiar with. [Install Git][git] if you don't already have it installed. Then initialize your project as a git repo with `git init` in the root of your project (VSCode and any other number of tools can do this as well.) If you haven't already, create a .gitignore at the root of your project to ignore the stuff we don't want to commit. Go ahead and put this in there: @@ -29,3 +31,4 @@ coverage/ This will make it so these things won't get added to our repo. If you want more Git instruction, please check out [ThePrimeagen's course on Frontend Masters][prime]. [prime]: https://frontendmasters.com/courses/everything-git/ +[git]: https://git-scm.com/book/en/v2/Getting-Started-Installing-Git diff --git a/lessons/03-tools/E-vite.md b/lessons/03-tools/E-vite.md index d428d4f..dbe22ac 100644 --- a/lessons/03-tools/E-vite.md +++ b/lessons/03-tools/E-vite.md @@ -11,6 +11,9 @@ keywords: - JavaScript - frontend development --- + +## Vite + The build tool we are going to be using today is called [Vite][vite]. Vite (pronounced "veet", meaning quick in French) is a tool put out by the Vue team that ultimately ends up wrapping [Rollup][rollup] which does the actual bundling. The end result is a tool that is both easy to use and produces a great end result. > Fun fact: there's a new project being developed called [Rolldown][rolldown] which is written in Rust and aims to replace Rollup. @@ -49,7 +52,7 @@ export default defineConfig({ }); ``` -By default, Vite will find the index.html file in where-ever the root is and treat it as the head of a source graph. It'll crawl all your HTML, CSS, and JavaScript you link to from there and create your project for you. We don't have to do any more configuration than that. Vite will take care of the rest. +By default, Vite will look for the index.html file in the root directory and treat it as the head of a source graph. It'll crawl all your HTML, CSS, and JavaScript you link to from there and create your project for you. We don't have to do any more configuration than that. Vite will take care of the rest. Okay, let's _actually_ install React to our project. @@ -87,7 +90,7 @@ Instead of /public/style.css, use /style.css.` – ignore this, we'll fix it in `dev` will start the development server, typically on [http://localhost:5173/](). `build` will prepare static files to be deployed (to somewhere like GitHub Pages, Vercel, Netlify, AWS S3, etc.) `preview` lets you preview your production build locally. -> Do note that we've changed domains here. By default Vite uses localhost:5173. Fun fact, 5173 sort of spells VITE if you make the 5 its Roman Numeral version, V. +> Note that we've changed domains here. By default Vite uses localhost:5173. Fun fact, 5173 sort of spells VITE if you make the 5 its Roman Numeral version, V. ## Alternatives diff --git a/lessons/04-core-react-concepts/A-jsx.md b/lessons/04-core-react-concepts/A-jsx.md index c39c374..74ad54a 100644 --- a/lessons/04-core-react-concepts/A-jsx.md +++ b/lessons/04-core-react-concepts/A-jsx.md @@ -16,7 +16,9 @@ keywords: - Vite --- -So far we've been writing React without JSX, something that I don't know anyone that actually does with their apps. _Everyone_ uses JSX. I show you this way so what JSX is actually doing is demystified to you. It doesn't do hardly anything. It just makes your code a bit more readable. +## JSX + +So far we've been writing React without JSX, something that I don't know anyone that actually does with their apps. _Everyone_ uses JSX. I've shown you the `createElement` way so you'll understand what JSX is actually doing. It doesn't do hardly anything. It just makes your code a bit more readable. If I write `React.createElement("h1", { id: "main-title" }, "My Website");`, what am I actually trying to have rendered out? `

My Website

`, right? What JSX tries to do is to shortcut this translation layer in your brain so you can just write what you mean. @@ -56,9 +58,10 @@ npm i -D eslint-plugin-react@7.37.1 Then in your eslint.config.mjs ```javascript -// at top +// at top, import the ESLint reactPlugin +import reactPlugin from "eslint-plugin-react"; -// under js.configs.recommended +// under js.configs.recommended line { ...reactPlugin.configs.flat.recommended, settings: { @@ -72,13 +75,15 @@ reactPlugin.configs.flat["jsx-runtime"], // add to files files: ["**/*.js", "**/*.jsx"], // add JSX -// inside the same object files, top level field +// inside the same object files, after the languageOptions: rules: { "react/no-unescaped-entities": "off", "react/prop-types": "off", }, ``` +You can also copy fill config from [the repo][eslint]. + We have to add two configs, one to allow ESLint to understand React and add some basic React rules, and one to modernize it as React 17 changed a bit how ESLint interacts with React. We're also turning off two rules that I don't find particularly useful: unescaped entites (which make you change things like `'` into `&apos`) and prop types which no one has used in a decade at this point. Otherwise we should be good to go. @@ -114,7 +119,7 @@ const App = () => { const container = document.getElementById("root"); const root = createRoot(container); -root.render(React.createElement(App)); +root.render(); ``` Also head over to index.html and change the script tag @@ -127,6 +132,8 @@ Notice we have Pizza as a component. Notice that the `P` in `Pizza` is capitaliz We now pass props down as we add attributes to an HTML tag. Pretty cool. +You can test your app by running `npm run dev` and opening the URL shown in the terminal. It's typically [http://localhost:5173/]() + ## The API / Image Server For this course we will use a little Fastify server I built for you. It's in the [api][api] @@ -153,7 +160,9 @@ export default defineConfig({ }); ``` -Now run your API server by running api/server.js by running `node api/server.js`. Note that this server is _outside_ your project directory. If you're in the project directory, you'll have to run `node ../api/server.js`. You need both servers running at the same time. Your Vite server will intercept `api` and `public` calls and reroute them to your API server! +## Run the API Server + +Open a new terminal and navigate to the `api` directory. Note that this server is _outside_ your project directory. You'll need to install the dependencies with `npm install`. After that, you can start the server by running `node api/server.js`. You need both servers running at the same time. Your Vite server will intercept `api` and `public` calls and reroute them to your API server! Now let's add images to our Pizza. @@ -183,3 +192,4 @@ And now you should have images! [standard]: https://standardjs.com/ [step]: https://github.com/btholt/citr-v9-project/tree/master/03-jsx [api]: https://github.com/btholt/citr-v9-project/tree/main/api +[eslint]: https://github.com/btholt/citr-v9-project/blob/main/03-jsx/eslint.config.mjs diff --git a/lessons/04-core-react-concepts/B-hooks.md b/lessons/04-core-react-concepts/B-hooks.md index 2f7b273..e03d4c3 100644 --- a/lessons/04-core-react-concepts/B-hooks.md +++ b/lessons/04-core-react-concepts/B-hooks.md @@ -14,6 +14,8 @@ keywords: - Brian Holt --- +## Hooks + Okay, so now we want to make it so people can add pizzas to their order. We need a little form that allows them to select the pizza and the size. Create a new file called Order.jsx and add the following: ```javascript @@ -93,7 +95,7 @@ Now add it to your App.jsx: import Order from "./Order"; // in App.jsx, replace all the Pizzas -; + ``` > 🚨 You'll have some errors in the console, that's okay. @@ -134,7 +136,7 @@ onChange={(e) => setPizzaSize(e.target.value)} - `useState` returns to us an array with two things in it: the current value of that state and a function to update that state. We're using a feature of JavaScript called destructuring to get both of those things out of the array. - We use the `setPizzaType` / `setPizzaSize` function in the `onChange` attribute of the input. Every time the input is typed into, it's going to call that functions which call `setPizzaType` and `setPizzaSize` with what has been typed into the input or what has been selected or what has been clicked on. When `setPizzaType` and `setPizzaSize` are called, React knows that its state has been modified and kicks off a re-render. - You can make your own custom hooks; `useState` is just one of many. -- Historically, React has been written using `class`es with state being on the instance of the component. This is still a supported pattern in React. We'll see how to do it later. +- Historically, React has been written using classes with state being on the instance of the component. This is still a supported pattern in React. We'll see how to do it later. - We could have put an onChange handler on each of the radio buttons. However event bubbling works the same in React as it does in the normal DOM and we could put it directly on the div that encapsulates all the radio buttons and just have to do it once. - You can use `useState` as many times as you need for various pieces of state! Again, this is why ordering is important because React relies on `useState` to be called in strictly the same order every time so it can give you the same piece of state. - Similar to above. We're using `onChange` and `onBlur` because it makes it more accessible. @@ -142,7 +144,7 @@ onChange={(e) => setPizzaSize(e.target.value)} > I'm showing you how to do a "controlled form" in that we're using hooks to control each part of the form. In reality, it'd be better to leave these _uncontrolled_ (aka don't set the value) and wrap the whole thing in a form. Then we can listen for submit events and use that event to gather info off the form. This is less code and less burdensome to write. If you have a standard form sort of thing to write, do that as an uncontrolled form. If you need to do dynamic validation, react to a user typing a la typeahead (functionality that provides real-time suggestions), or something of the ilk, then a controlled input is perfect, otherwise stick to uncontrolled. > Also what's new in React is called a "form action" that is considered unstable. In the future you will just add `
[…]
` and a form action will handle the entire form for you. -Another side note: event bubbling works in React works just like you would expect. In theory you can have mega event handler in React but the lint rules and React's dev tools get noisy about it if you do it that way so I tend to just follow their recommendation. +Another side note: event bubbling in React works just like you would expect. In theory you can have mega event handler in React but the lint rules and React's dev tools get noisy about it if you do it that way so I tend to just follow their recommendation. ```javascript // you could replace the div surrounding the radio buttons and remove all the onChange handlers @@ -152,5 +154,5 @@ Another side note: event bubbling works in React works just like you would expec > 🏁 [Click here to see the state of the project up until now: 04-hooks][step] [babel]: https://babeljs.io/ -[step]: https://github.com/btholt/citr-v8-project/tree/master/04-hooks +[step]: https://github.com/btholt/citr-v9-project/tree/main/04-hooks [js-api]: https://developer.mozilla.org/en-US/docs/Web/API/Element/className diff --git a/lessons/04-core-react-concepts/C-effects.md b/lessons/04-core-react-concepts/C-effects.md index d2acb6d..c640190 100644 --- a/lessons/04-core-react-concepts/C-effects.md +++ b/lessons/04-core-react-concepts/C-effects.md @@ -14,7 +14,9 @@ keywords: - Brian Holt --- -We have enough of an app to start making some API requests now. We want the app to request an initial set of pets on initial load of the page. So let's make that happen using a special hook called `useEffect`. `useEffect` allows you to say do a render of this component first so the user can see _something_ then as soon as the render is done, _then_ do something (the something here being an effect). In our case, we want the user to see our UI first then we want to make a request to the API so we can initialize a list of pizzas. +## Effects + +We have enough of an app to start making some API requests now. We want the app to request an initial set of pizzaTypes on initial load of the page. So let's make that happen using a special hook called `useEffect`. `useEffect` allows you to say do a render of this component first so the user can see _something_ then as soon as the render is done, _then_ do something (the something here being an effect). In our case, we want the user to see our UI first then we want to make a request to the API so we can initialize a list of pizzas. > Make sure you have both your Vite dev server running _and_ your API server running. Both. @@ -69,9 +71,8 @@ async function fetchPizzaTypes() { // replace and button at the end ``` -- We're taking advantage of closures here that if we define the requestPets function _inside_ of the render that it will have access to that scope and can use all the hooks there. - We put all the logic for fetching pizza types in an async function to make it more readable. You can't make the function provided to useEffect async. -- the `[]` at the end of the useEffect is where you declare your data dependencies. React wants to know _when_ to run that effect again. You don't give it data dependencies, it assumes any time any hook changes that you should run the effect again. This is bad because that would mean any time setPets gets called it'd re-run render and all the hooks again. See a problem there? It'd run infinitely since requestPets calls setPets. +- the `[]` at the end of the useEffect is where you declare your data dependencies. React wants to know _when_ to run that effect again. You don't give it data dependencies, it assumes any time any hook changes that you should run the effect again. This is bad because that would mean any time setPizzaTypes gets called it'd re-run render and all the hooks again. See a problem there? It'd run infinitely since fetchPizzaTypes calls setPizzaTypes. - You can instead provide which hooks to watch for changes for. In our case, we actually only want it to run once, on creation of the component, and then to not run that effect again. (we'll do searching later via clicking the submit button) You can accomplish this only-run-on-creation by providing an empty array. - We're using a loading flag to only display data once it's ready. We'll use TanStack Query in a bit to make this code look cleaner. But this is how you do conditional showing/hiding of components in React. - The `key` portion is an interesting one. When React renders arrays of things, it doesn't know the difference between something is new and something is just being re-ordered in the array (think like changing the sorting of a results list, like price high-to-low and then priced low-to-high). Because of this, if you don't tell React how to handle those situations, it just tears it all down and re-renders everything anew. This can cause unnecessary slowness on devices. This is what key is for. Key tells React "this is a simple identifier of what this component is". If React sees you just moved a key to a different order, it will keep the component tree. So key here is to associate the key to something unique about that component. 99/100 this is a database ID of some variety. _Don't_ use the index of the array as that just isn't right unless the array is literally is never going to change order. diff --git a/lessons/04-core-react-concepts/D-dev-tools.md b/lessons/04-core-react-concepts/D-dev-tools.md index e44b6ca..e957562 100644 --- a/lessons/04-core-react-concepts/D-dev-tools.md +++ b/lessons/04-core-react-concepts/D-dev-tools.md @@ -25,12 +25,12 @@ Why is it important that we strip the debug stuff out? The dev bundle of React i ## Strict Mode -React has a new strict mode. If you wrap your app in `` it will give you additional warnings about things you shouldn't be doing. I'm not teaching you anything that would trip warnings from `StrictMode` but it's good to keep your team in line and not using legacy features or things that will be soon be deprecated. +React has a new strict mode. If you wrap your app in `` it will give you additional warnings about things you shouldn't be doing. I'm not teaching you anything that would strip warnings from `StrictMode` but it's good to keep your team in line and not using legacy features or things that will be soon be deprecated. Be aware that `StrictMode` continually double-renders your components and will run effects twice. It does this catch subtle bugs where your app will change between renders when it's not meant to. It can be helpful, but to be honest, once you learn to write React the correct way you'll nearly never hit that sort of bug. ## Dev Tools -React has wonderful dev tools that the core team maintains. They're available for both Chromium-based browsers and Firefox. They let you do several things like explore your React app like a DOM tree, modify state and props on the fly to test things out, tease out performance problems, and programtically manipulate components. Definitely worth downloading now. [See here][dev-tools] for links. +React has wonderful dev tools that the core team maintains. They're available for both Chromium-based browsers and Firefox. They let you do several things like explore your React app like a DOM tree, modify state and props on the fly to test things out, tease out performance problems, and programmatically manipulate components. Definitely worth downloading now. [See here][dev-tools] for links. [dev-tools]: https://react.dev/learn/react-developer-tools diff --git a/lessons/04-core-react-concepts/E-custom-hooks.md b/lessons/04-core-react-concepts/E-custom-hooks.md index 347cea7..11a47de 100644 --- a/lessons/04-core-react-concepts/E-custom-hooks.md +++ b/lessons/04-core-react-concepts/E-custom-hooks.md @@ -12,11 +12,14 @@ keywords: - pizza of the day - debug value --- + +## Custom Hooks + One thing that's pretty special about hooks is their composability. You can use hooks to make other hooks! People tend to call these custom hooks. There are even people who go as far to say "never make an API request in a component, always do it in a hook." I don't know if I'm as hardcore as that but I see the logic in it. If you make a custom hook for those sorts of things they become individually testable and do a good job to separate your display of data and your logic to acquire data. I'm more in the camp of make custom hooks for either complicated logic or reusable logic, but for simple cases it's okay to keep things simple. Okay, so we want to add a "Pizza of the Day" banner at the bottom of our page. This necessitates calling a special API to get the pizza of the day (which should change every day based on your computer's time.) Let's first write the component that's going to use it. -Make a file called PizzaOftheDay.jsx +Make a file called PizzaOfTheDay.jsx ```javascript import { usePizzaOfTheDay } from "./usePizzaOfTheDay"; @@ -92,7 +95,7 @@ Lastly, let's go add this to App.jsx so our component renders. import PizzaOfTheDay from "./PizzaOfTheDay"; // under -; + ``` You should now see the new component which uses our new hook at the bottom! diff --git a/lessons/04-core-react-concepts/F-handling-user-inputs.md b/lessons/04-core-react-concepts/F-handling-user-inputs.md index fc4701d..095e90d 100644 --- a/lessons/04-core-react-concepts/F-handling-user-inputs.md +++ b/lessons/04-core-react-concepts/F-handling-user-inputs.md @@ -14,6 +14,7 @@ keywords: - Cart.jsx - web development --- + So now we want to be able to handle the user's cart and submitting our order. Let's go add what we need to Order.jsx ```javascript @@ -35,7 +36,7 @@ const [cart, setCart] = useState([]); // just inside the last closing div { - loading ?

LOADING …

: ; + loading ?

LOADING …

: } ``` @@ -102,3 +103,7 @@ async function checkout() { ``` Now we can pass that checkout function in and whenever someone clicks inside the form, it will run the checkout function from the Order components. We're doing a simple loading animation, doing a fetch, and then clearing the status once we're all done. Not too bad! + +> 🏁 [Click here to see the state of the project up until now: 07-handling-forms][step] + +[step]: https://github.com/btholt/citr-v9-project/tree/master/07-handling-forms \ No newline at end of file diff --git a/lessons/04-core-react-concepts/G-context.md b/lessons/04-core-react-concepts/G-context.md index 4936881..b895045 100644 --- a/lessons/04-core-react-concepts/G-context.md +++ b/lessons/04-core-react-concepts/G-context.md @@ -12,6 +12,9 @@ keywords: - global state - Brian Holt --- + +## Context + Let's make a cart indicator on the top right of the page. Create a file called Header.jsx and put this in there. ```javascript @@ -34,7 +37,7 @@ Now let's use that in App.jsx import Header from "./Header"; // replace .logo -
; +
``` Right now this will always show 5 but we want that number in .nav-cart-number to reflect how many items we have in our cart. How would we do that? We could move all of cart and its hooks to App.jsx and pass it into both Header and Order. In an app this small, that could be the right choice. But let's look at another way to do it, context. @@ -55,7 +58,7 @@ Okay, let's go put it in App.jsx. ```javascript // at the top -import React, { StrictMode, useState } from "react"; // need useState +import { StrictMode, useState } from "react"; // need useState import { CartContext } from "./contexts"; // replace App @@ -105,4 +108,8 @@ const [cart] = useContext(CartContext); That's it! We don't need the setCart function so we don't import that. Otherwise all looks pretty normal! -So that's context. It's superful for stuff like this where we don't want to "prop drill" our state everywhere. Since cart is now being held at the App level, we definitely could have just said `
` and called it a day, and in this specific use case I would have. But now imagine if Header was super deeply nested and you had to pass that state down a lot of children components – it gets messy quickly. This is what we call prop drilling. React's explicit data flow is a feature, not a bug. It makes where data came from and where data is going very readable. But it can make your code verbose in a non-helpful way, and context is an escape hatch for that. In general, use context sparingly and only where it's _really_ inconvenient to just do it the normal way of using props. +So that's context. It's super useful for stuff like this where we don't want to "prop drill" our state everywhere. Since cart is now being held at the App level, we definitely could have just said `
` and called it a day, and in this specific use case I would have. But now imagine if Header was super deeply nested and you had to pass that state down a lot of children components – it gets messy quickly. This is what we call prop drilling. React's explicit data flow is a feature, not a bug. It makes where data came from and where data is going very readable. But it can make your code verbose in a non-helpful way, and context is an escape hatch for that. In general, use context sparingly and only where it's _really_ inconvenient to just do it the normal way of using props. + +> 🏁 [Click here to see the state of the project up until now: 08-context][step] + +[step]: https://github.com/btholt/citr-v9-project/tree/master/08-context \ No newline at end of file diff --git a/lessons/05-ecosystem/A-tanstack-router.md b/lessons/05-ecosystem/A-tanstack-router.md index ec91901..d9dbf4d 100644 --- a/lessons/05-ecosystem/A-tanstack-router.md +++ b/lessons/05-ecosystem/A-tanstack-router.md @@ -13,6 +13,8 @@ keywords: - Brian Holt --- +## Tanstack Router + So now we have arrived to the point where we want multiple pages in our app. We need some sort of router tool to accomplish that. We _could_ just work with the browser's history API to do it but there so many ways to get it wrong, plus we are spoiled for choice when it comes to great React routing libraries. Historically I have taught [react-router][rr] as part of this course. It has been around for a long time and is well tested. Nearly every version of this course uses it (there was a brief while where it was Reach Router, but that has since been merged back into React Router.) It is a great tool and it underpins Remix, a great full-stack React framework. Remix and React Router are actually merging and will eventually be the same thing. [v8 of this course][v8] use React Router if you want to learn more about it from me. @@ -21,7 +23,7 @@ Historically I have taught [react-router][rr] as part of this course. It has bee Today we are going to be using [TanStack Router][tsr], another amazing router. Why the switch? Two reasons: -- This is a client-side focused course, and TanStack Router is made for client-side use cases. React Router and Remix have shifted a lot of focus to a more full-stack experience (though it does still work client-side only.) +- This is a client-side focused course, and TanStack Router is made for client-side use cases. React Router and Remix have shifted a lot of focus to a more full-stack experience (though they do still work client-side only). - In Intermediate React we will be covering more full-stack use cases, in which I will using Next.js to show off those features. So, suffice to say, you have a lot of good choices. I'm a fan of TanStack Router and I think it's a great tool for you to get started with. Let's dive in. @@ -29,8 +31,8 @@ So, suffice to say, you have a lot of good choices. I'm a fan of TanStack Router Let's start by installing it. ```bash -npm install @tanstack/react-router -npm install -D @tanstack/router-plugin @tanstack/router-devtools +npm install @tanstack/react-router@1.65.0 +npm install -D @tanstack/router-plugin@1.65.0 @tanstack/router-devtools@1.65.0 ``` We need to install the router and then we'll be using its code generation tool as well as its dev tools (which are dev-only, hence the -D). Let's add it first to our vite.config.js. @@ -165,17 +167,20 @@ import { Link } from "@tanstack/react-router"; // surround the logo with a link

Padre Gino's Pizza

-; + ``` This should all work now. Open your app and see if it loads. - Notice you get a little flash before it loads. It's lazy-loading your route. If this was a real app, we'd make a more pleasant loading experience. I'll leave that as an exercise for you to do. -- So, let's talk about the cart. Go to the order page, add a bunch of stuff to cart, and head back to the home page. Notice there's still stuff in our cart! Head back to the order page and see it's still there. That's because \_\_root.jsx (where our cart state lives) never gets unrendered. If this just lived in Cart.jsx or order.lazy.jsx, it'd be blown away whenever you navigate away. Why? Because when the component gets unrendered, it loses all of its state. This is one of the reasons Context can be super powerful. +- So, let's talk about the cart. Go to the order page, add a bunch of stuff to cart, and head back to the home page. Notice there's still stuff in our cart! Head back to the order page and see it's still there. That's because \_\_root.jsx (where our cart state lives) never gets un-rendered. If this just lived in Cart.jsx or order.lazy.jsx, it'd be blown away whenever you navigate away. Why? Because when the component gets un-rendered, it loses all of its state. This is one of the reasons Context can be super powerful. - Take a gander at the dev tools being rendered in the bottom left (don't worry, it gets stripped out automatically in production.) Click into and peruse around. Lots of cool stuff here for free. You did it! That's how easy routing has become. It's come so far from the early days of React. +> 🏁 [Click here to see the state of the project up until now: 09-router][step] + +[step]: https://github.com/btholt/citr-v9-project/tree/master/09-router [rr]: https://reactrouter.com/ [v8]: https://react-v8.holt.courses/lessons/react-capabilities/react-router [tsr]: https://tanstack.com/router/latest diff --git a/lessons/05-ecosystem/B-tanstack-query.md b/lessons/05-ecosystem/B-tanstack-query.md index 237bdcb..54c88f8 100644 --- a/lessons/05-ecosystem/B-tanstack-query.md +++ b/lessons/05-ecosystem/B-tanstack-query.md @@ -16,6 +16,8 @@ keywords: - Brian Holt --- +## Tanstack Query + Let's make make a past orders page. Create a new file, `past.lazy.jsx`. If your Vite server is already running, it will automatically stub it out for you! Pretty cool developer experience from TanStack. Now let's install our next tool, [TanStack (React) Query][tsq]. @@ -46,7 +48,7 @@ Let's also add the dev tools, like we did for the router. In `src/routes/__root. import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; // under router dev tools -; + ``` Now in the bottom right of your window you'll see a 🏝️. Click on that and we'll open the dev tools. We haven't used the dev tools yet so it'll be empty. @@ -132,4 +134,7 @@ function PastOrdersRoute() { That's it! React Query is both simple to use and super flexible to handle a tough problem. It's one of my favorite libraries for React. +> 🏁 [Click here to see the state of the project up until now: 10-query][step] + +[step]: https://github.com/btholt/citr-v9-project/tree/master/10-query [tsq]: https://tanstack.com/query/latest diff --git a/lessons/06-advanced-react/A-portals.md b/lessons/06-advanced-react/A-portals.md index 54f802d..8c00462 100644 --- a/lessons/06-advanced-react/A-portals.md +++ b/lessons/06-advanced-react/A-portals.md @@ -13,6 +13,9 @@ keywords: - web development - Brian Holt --- + +## Portals + What if you are rendering a page and you want to render something in another part of the page at the same time? Think like you have a contextual right navigation and you have a page that wants to render into the contextual nav. We could use something like context to do that and that would be totally acceptable. But there's another cool way called a portal to do this as well. It even lets us render outside our app totally, like we're about to do. We're going to do a modal or a "popover". I think these are bad user experiences but it's a perfect use case for a portal so we're going to do it! In our case, because we want the modal to render in front of everything, we need the div that the modal renders into to be first in the DOM. So let's go make a div that does that. Open your index.html file and put this in there. @@ -131,3 +134,7 @@ const { isLoading: isLoadingPastOrder, data: pastOrderData } = useQuery({ - When a user clicks Close, we set the focusedOrder to be undefined again which causes the Modal to unrender. That's it! + +> 🏁 [Click here to see the state of the project up until now: 11-modals][step] + +[step]: https://github.com/btholt/citr-v9-project/tree/master/11-modals \ No newline at end of file diff --git a/lessons/06-advanced-react/B-error-boundaries.md b/lessons/06-advanced-react/B-error-boundaries.md index b034a37..b30a0d0 100644 --- a/lessons/06-advanced-react/B-error-boundaries.md +++ b/lessons/06-advanced-react/B-error-boundaries.md @@ -16,7 +16,10 @@ keywords: - legacy React - Complete Intro to React --- -Frequently there's errors with APIs with malformatted or otherwise weird data. Let's be defensive about this because we still want to use this API but we can't control when we get errors. We're going to use a feature called `componentDidCatch` to handle this. This is something you can't do with hooks so if you needed this sort of functionality you'd have to use a class component. + +## Error Boundaries + +Frequently there's errors with APIs with malformed or otherwise weird data. Let's be defensive about this because we still want to use this API but we can't control when we get errors. We're going to use a feature called `componentDidCatch` to handle this. This is something you can't do with hooks so if you needed this sort of functionality you'd have to use a class component. This will also catch 404s on our API if someone give it an invalid ID! diff --git a/lessons/06-advanced-react/C-uncontrolled-forms.md b/lessons/06-advanced-react/C-uncontrolled-forms.md index 05bf83d..37855fd 100644 --- a/lessons/06-advanced-react/C-uncontrolled-forms.md +++ b/lessons/06-advanced-react/C-uncontrolled-forms.md @@ -11,6 +11,9 @@ keywords: - React - Brian Holt --- + +## Uncontrolled Forms + We are now going to do a contact page for our site. As part of that, we will accept a name, email, and message from our users and submit it our backend. Like all good backends, ours will log it to the console and then ignore it. We are going to see two new concepts here: how to do a post with TanStack Query (which they call a mutation) and how to do uncontrolled forms with React. Let's start with our API query. In src/api, create a file called postContact.js and put this in there. diff --git a/lessons/07-testing/B-basic-react-tests.md b/lessons/07-testing/B-basic-react-tests.md index a48a171..860e238 100644 --- a/lessons/07-testing/B-basic-react-tests.md +++ b/lessons/07-testing/B-basic-react-tests.md @@ -46,7 +46,7 @@ test("alt text renders on image", async () => { This is your most basic test. It renders a React component, and then starts running assertions on it. Here we're asserting that our pizza image ends up with the correct alt text on it. Not the most useful test but a good start for us seeing how to assert something. I could see this being useful if we had bugs that occasionally alt text wasn't showing up. -We're using getByRole here to grab the image on the page. In general React Testing Library wants you to adopt a user-centric mindset of how you're asserting things on the page. In thise case we're trying to find an image which is something a user would see. Contrast that with using a CSS selector to select the image (which you can do, it's just frowned upon) which is very implementation-centric (a user doesn't know nor care about CSS classes.) +We're using getByRole here to grab the image on the page. In general React Testing Library wants you to adopt a user-centric mindset of how you're asserting things on the page. In this case we're trying to find an image which is something a user would see. Contrast that with using a CSS selector to select the image (which you can do, it's just frowned upon) which is very implementation-centric (a user doesn't know nor care about CSS classes.) Let's add another test to make sure that we have a default image if pizza isn't passed an image. diff --git a/lessons/07-testing/F-coverage.md b/lessons/07-testing/F-coverage.md index d9db12a..4050458 100644 --- a/lessons/07-testing/F-coverage.md +++ b/lessons/07-testing/F-coverage.md @@ -44,8 +44,5 @@ Lastly, add `coverage/` to your `.gitignore` since this shouldn't be checked in. v8 use Chrome's built-in code coverage capabilities to run your tests which makes it significantly faster and outputs it in a way that all of [Istanbul][istanbul] tools work with it. You can tell Vitest to use Istanbul but unless you have a very specific reason to, just use v8. -> 🏁 [Click here to see the state of the project up until now: testing][step] - -[step]: https://github.com/btholt/citr-v8-project/tree/master/testing [istanbul]: https://istanbul.js.org/ [c8]: https://github.com/bcoe/c8 diff --git a/lessons/08-whats-next/A-react-19.md b/lessons/08-whats-next/A-react-19.md index 6779ac3..7f721fe 100644 --- a/lessons/08-whats-next/A-react-19.md +++ b/lessons/08-whats-next/A-react-19.md @@ -12,6 +12,9 @@ keywords: - web components - use client --- + +## React 19 + This course has been centered on version 18.3. The nice thing about React is that while they do make breaking changes, they are usually fairly intentional about it and do so sparingly. In other words, what you learned today will still be useful five years from now. You could take the version of the course I taught five years ago and still be pretty up to date on how to write React. In other words, despite the fact that React 19 is imminent (or perhaps even out by the time you read this), there's no need to panic. All the stuff they are adding will just complement what you have learned and not change it. Do keep an eye out for version 6 of my Intermediate React course. We will cover a lot of the React 19 features in that course. @@ -26,7 +29,7 @@ Let's start with one of the bigger shifts in React. We will cover this _extensiv For server components, we can do things like reference server-side secrets, query databases, call private APIs, or other things that we want data for our client side app but we can't query client-side without risking leaking secrets or DDOS. All of the logic will be executed server-side and then the finished component will be sent down to the client. Pretty cool, right? -They're even taking it step further with taintUniqueValue and taintObjectReference. These allow a develoepr to say "hey, this is super sensitive information, make sure this never is sent to the client". You would use this with like an API key, a database connection string, or some other secret that you wouldn't want to accidentally leak. I love this because what if a developer changes a 'use server' directive to a 'use client' directive? They may not notice and send it to production accidentally, causing a data breach. With the concept of taint, you can make sure that never happens. +They're even taking it step further with taintUniqueValue and taintObjectReference. These allow a developer to say "hey, this is super sensitive information, make sure this never is sent to the client". You would use this with like an API key, a database connection string, or some other secret that you wouldn't want to accidentally leak. I love this because what if a developer changes a 'use server' directive to a 'use client' directive? They may not notice and send it to production accidentally, causing a data breach. With the concept of taint, you can make sure that never happens. 'use client' is the opposite – something that would never make sense to render server-side. Think like a highly interactive component like a text editor or a maps widget or something like that – it's meant to be a client-side experience so you better off just passing all the down to the client immediately and let them do the rendering. Keep in mind that you don't need to use this directive unless you are doing server-side rendering. By default it will still do client-side rendering. @@ -47,7 +50,7 @@ Now whenever MyPizza gets rendered it will change the title of the doc (which wh ## preload and preconnect -Two pretty helpful functions to help give hints to the browser of what it may want to start loading in the background. If you are not familiar with the concepts, [head here][preload] to read more, but long story short it's for things like images, fonts, scripts, stylesheets, etc. You can let the browser know "hey, we're likely to going to need load these soon, can you either preconnect to this URL to get ready to download it or just go ahead and preload it?" This will allow your user to have more instantaneous expereiences since they won't need to wait for these things to load when required. Keep in mind these are hints though and the browser can ignore them. A good of example of this could be that your phone is in low-power mode, it may ignore those hints as it wants to save battery. +Two pretty helpful functions to help give hints to the browser of what it may want to start loading in the background. If you are not familiar with the concepts, [head here][preload] to read more, but long story short it's for things like images, fonts, scripts, stylesheets, etc. You can let the browser know "hey, we're likely to going to need load these soon, can you either preconnect to this URL to get ready to download it or just go ahead and preload it?" This will allow your user to have more instantaneous experiences since they won't need to wait for these things to load when required. Keep in mind these are hints though and the browser can ignore them. A good of example of this could be that your phone is in low-power mode, it may ignore those hints as it wants to save battery. ```javascript const EnterConfirmation = () => { @@ -62,7 +65,7 @@ const EnterConfirmation = () => { ## Custom components (aka web components) -It's always been possible, in theory, to use web components with React, but it's been a huge pain in the butt, involving refs and other careful planning to make sure you don't accidentally unrender and re-render it. Suffice to say, it was possible but impractical. +It's always been possible, in theory, to use web components with React, but it's been a huge pain in the butt, involving refs and other careful planning to make sure you don't accidentally un-render and re-render it. Suffice to say, it was possible but impractical. Now with React 19 it will just handle all of that caretaking of the custom elements for you, just treat it like any other normal tag and React will do all the babysitting for you. It's certainly a big step in making custom elements that work across Angular, React, Svelte, etc. but who knows, we've been talking about custom elements my whole career (seriously, I remember seeing a Polymer talk at the first Fluent conf I went to) and it still isn't here yet. diff --git a/lessons/08-whats-next/B-form-actions.md b/lessons/08-whats-next/B-form-actions.md index 75793fd..558dce5 100644 --- a/lessons/08-whats-next/B-form-actions.md +++ b/lessons/08-whats-next/B-form-actions.md @@ -12,6 +12,9 @@ keywords: - Brian Holt - React upgrade --- + +## Form Actions + Okay, so let's actually upgrade our app to be React 19. > 🚨 Pay attention here, as you need to figure out which version of React to install. @@ -50,7 +53,7 @@ function addToCart() {
[…]
; ``` -Same as above. But here's what cool if you're using somethingl like Next.js or Remix: we can use the `'use server'` directive here and make this a _server_ action. Something like +Same as above. But here's what cool if you're using something like Next.js or Remix: we can use the `'use server'` directive here and make this a _server_ action. Something like ```javascript function addToCart(formData) { diff --git a/lessons/08-whats-next/C-use-and-suspense.md b/lessons/08-whats-next/C-use-and-suspense.md index 89579e2..27a55c7 100644 --- a/lessons/08-whats-next/C-use-and-suspense.md +++ b/lessons/08-whats-next/C-use-and-suspense.md @@ -14,6 +14,8 @@ keywords: - async rendering --- +## use and Suspense + We've been talking about React Suspense for a long time, and technically still we are not suppose to use it directly. But we React 19 we will get some blessed ways to start using it. (Some libraries already use it.) The idea behind Suspense is that a React component can start rendering and then suspend itself if all its data hasn't loaded yet. This is cool because your code reads really well: it basically looks like you're assuming the data is already there, and React takes care of getting it behind the scene. diff --git a/lessons/08-whats-next/D-react-compiler.md b/lessons/08-whats-next/D-react-compiler.md index 5b1c231..82aafc7 100644 --- a/lessons/08-whats-next/D-react-compiler.md +++ b/lessons/08-whats-next/D-react-compiler.md @@ -13,6 +13,9 @@ keywords: - optimization - React development --- + +## React Compiler + We didn't talk much at all about performance hacks with React but there are a few. Generally speaking, React is in its "unoptimized" state is fast enough and adding these performance hacks on top make shave a millisecond here or there off, but in general aren't worth doing unless it's a big gain because it makes it harder to code with. Specifically I'm talking about [useMemo][memo] and [useCallback][callback]. These two tools allow you to control when React will re-render your app. If you're rendering a massive spreadsheet and it's not changing frequently but its parent is, you can use useMemo to say "hey, this doesn't need to re-render unless this condition is true". This only really helps when it's a big thing like this. I don't even teach this in this course because you use it very infrequently and some newer devs get tempted to use it all the time. It will make it hard later to understand why some things are rendering and some aren't. It makes bugs harder to find. diff --git a/lessons/09-wrap-up/A-congrats.md b/lessons/09-wrap-up/A-congrats.md index a02d93b..f5bcf28 100644 --- a/lessons/09-wrap-up/A-congrats.md +++ b/lessons/09-wrap-up/A-congrats.md @@ -20,10 +20,10 @@ You are now fully equipped to write React professionally. Everything you learned So what should you do from here? -- [Intermediate React v5][v5] is still very relevant! Take this course and learn more about Tailwind, server-side rendering, more hooks, Redux, and a bunch of ther cool things. I'll update this when the new Intermediate React v6 comes out. +- [Intermediate React v5][v5] is still very relevant! Take this course and learn more about Tailwind, server-side rendering, more hooks, Redux, and a bunch of there cool things. I'll update this when the new Intermediate React v6 comes out. - [The rest of the Frontend Masters React learning path][path]. - Build your own app!! I cannot stress how important it is with concepts like this to just build. Take the dumbest idea in your head and make it. Nothing can replace experience! - - An ecommerce website to sell single socks so you can match unmatched socks + - An e-commerce website to sell single socks so you can match unmatched socks - An AI prompt text editor - A music app that lets you add Spotify, Apple Music, and Tidal songs to it. - A client to let you read Thread, X, and Bluesky in the same app. diff --git a/package-lock.json b/package-lock.json index d41172c..c346199 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "react-v9", + "name": "complete-intro-to-react-v9", "lockfileVersion": 3, "requires": true, "packages": {