diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/404.html b/404.html new file mode 100644 index 0000000..cda3995 --- /dev/null +++ b/404.html @@ -0,0 +1 @@ +404: This page could not be found

404

This page could not be found.

\ No newline at end of file diff --git a/CNAME b/CNAME new file mode 100644 index 0000000..24e21b2 --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +react-v9.holt.courses \ No newline at end of file diff --git a/_next/data/PxJrDJjmFmsKzZJwY10Qb/index.json b/_next/data/PxJrDJjmFmsKzZJwY10Qb/index.json new file mode 100644 index 0000000..4dd5d73 --- /dev/null +++ b/_next/data/PxJrDJjmFmsKzZJwY10Qb/index.json @@ -0,0 +1 @@ +{"pageProps":{"sections":[{"icon":"info-circle","title":"Welcome","slug":"welcome","lessons":[{"slug":"intro","fullSlug":"/lessons/welcome/intro","title":"Intro","order":"01A","path":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/01-welcome/A-intro.md","description":"Join Brian Holt in \"Complete Intro to React, v9,\" a beginner-friendly course that equips you with essential React skills using a first-principles approach, incorporating tools like Vite and JSX, making you job-ready in the React ecosystem."},{"slug":"my-setup","fullSlug":"/lessons/welcome/my-setup","title":"My Setup","order":"01B","path":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/01-welcome/B-my-setup.md","description":"Explore essential tools and configurations for the \"Complete Intro to React v9\" course by Brian Holt, including recommended Node.js version, Visual Studio Code setup with extensions, and terminal preferences for optimal React development experience. Learn about the use of tools like fnm for Node.js, Starship Prompt, MonoLisa font, and more within this comprehensive React course."}],"order":"01"},{"icon":"eye","title":"No Frills React","slug":"no-frills-react","lessons":[{"slug":"react-without-a-build-step","fullSlug":"/lessons/no-frills-react/react-without-a-build-step","title":"React without a Build Step","order":"02A","path":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/02-no-frills-react/A-react-without-a-build-step.md","description":"Learn the basics of creating a simple React app without using tools like Babel or Webpack. This tutorial guides you through setting up a project directory, writing pure JavaScript with React, and creating components to build a pizza ordering system. Ideal for beginners, this step-by-step guide by Brian Holt from the Complete Intro to React v9 course helps you understand fundamental React concepts using only essential tools."},{"slug":"components","fullSlug":"/lessons/no-frills-react/components","title":"Components","order":"02B","path":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/02-no-frills-react/B-components.md","description":"Learn how to separate React components into distinct files and make reusable, flexible components that accept props, transforming a static app into a dynamic one. This guide provides code examples in creating a React app featuring customizable Pizza components, enhancing React development skills. Discover these strategies in Brian Holt's Complete Intro to React, ideal for budding web developers."}],"order":"02"},{"icon":"screwdriver-wrench","title":"Tools","slug":"tools","lessons":[{"slug":"npm","fullSlug":"/lessons/tools/npm","title":"npm","order":"03A","path":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/03-tools/A-npm.md","description":"Learn about npm and pnpm, two essential package managers for Node.js, as you embark on your React development journey with Brian Holt's Complete Intro to React, v9. This section guides you through setting up npm projects and understanding the differences between npm and pnpm, enhancing your knowledge of JavaScript development tools."},{"slug":"code-formatting","fullSlug":"/lessons/tools/code-formatting","title":"Code Formatting","order":"03B","path":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/03-tools/B-code-formatting.md","description":"Learn how to maintain high code quality with Prettier by James Long, a powerful tool for consistent code formatting. Discover easy integration methods and how to use Prettier with Visual Studio Code to streamline your development process, as well as setup instructions for npm scripts in this guide from Brian Holt's Complete Intro to React course."},{"slug":"linting","fullSlug":"/lessons/tools/linting","title":"ESLint","order":"03C","path":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/03-tools/C-linting.md","description":"Learn how to set up and configure ESLint with Prettier for effective JavaScript code linting in your React projects, featuring tools like ESLint's JS config and globals package, under the guidance of seasoned developer Brian Holt."},{"slug":"git","fullSlug":"/lessons/tools/git","title":"Git","order":"03D","path":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/03-tools/D-git.md","description":"Learn how to efficiently set up a Git repository, create a .gitignore file, and manage untracked files in your project as part of the Complete Intro to React v9 course by Brian Holt. Enhance your Git skills with practical tips, ensuring a clean and professional project environment. For more detailed instruction, explore ThePrimeagen's Git course on Frontend Masters."},{"slug":"vite","fullSlug":"/lessons/tools/vite","title":"Vite","order":"03E","path":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/03-tools/E-vite.md","description":"Learn to set up your React project using Vite, a fast build tool preferred by the React community, with step-by-step guidance from Brian Holt. This tutorial covers the installation and configuration of Vite, React, and essential plugins to optimize your development environment."}],"order":"03"},{"icon":"book","title":"Core React Concepts","slug":"core-react-concepts","lessons":[{"slug":"jsx","fullSlug":"/lessons/core-react-concepts/jsx","title":"JSX","order":"04A","path":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/04-core-react-concepts/A-jsx.md","description":"Explore the integration of JSX in React applications with Brian Holt's \"Complete Intro to React, v9\" course, as it simplifies HTML tags into React.createElement calls, enhancing code readability. Learn about configuring ESLint for JSX, React components, and Vite server setup for a complete React development experience."},{"slug":"hooks","fullSlug":"/lessons/core-react-concepts/hooks","title":"Hooks","order":"04B","path":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/04-core-react-concepts/B-hooks.md","description":"Learn how to create an interactive form in React with useState hooks, allowing users to add pizzas to their order in this detailed coding guide from the \"Complete Intro to React, v9\" course by Brian Holt. Explore concepts like controlled inputs, JSX syntax, event handling, and the importance of state management in React development. Enhance your web development skills with practical React examples and create dynamic applications effectively."},{"slug":"effects","fullSlug":"/lessons/core-react-concepts/effects","title":"Effects","order":"04C","path":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/04-core-react-concepts/C-effects.md","description":"In this section of the Complete Intro to React v9 course by Brian Holt, learn how to use the `useEffect` hook to make API requests for initializing a list of pizzas, manage UI initial load with loading states, and optimize component updates using unique keys for rendering efficiency. Enhance your React projects with best practices for asynchronous data fetching and component optimization."},{"slug":"dev-tools","fullSlug":"/lessons/core-react-concepts/dev-tools","title":"Dev Tools","order":"04D","path":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/04-core-react-concepts/D-dev-tools.md","description":"Explore essential React development tools including NODE_ENV, Strict Mode, and React Dev Tools to enhance debugging, optimize performance, and maintain modern coding practices. Brian Holt's Complete Intro to React v9 offers practical insights into these tools for effective frontend development with Vite.js."},{"slug":"custom-hooks","fullSlug":"/lessons/core-react-concepts/custom-hooks","title":"Custom Hooks","order":"04E","path":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/04-core-react-concepts/E-custom-hooks.md","description":"Learn about creating custom hooks in React with Brian Holt's Complete Intro to React, covering how to build and integrate a custom hook for fetching pizza of the day using React hooks for effective separation and testability. Brian demonstrates building a component and using debug tools to inspect React custom hooks efficiently."},{"slug":"handling-user-inputs","fullSlug":"/lessons/core-react-concepts/handling-user-inputs","title":"Handling User Inputs","order":"04F","path":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/04-core-react-concepts/F-handling-user-inputs.md","description":"Learn how to handle user cart submissions and checkout process in React with Brian Holt's Complete Intro to React, v9. Build a shopping cart component using Order.jsx and Cart.jsx, manage cart state with hooks, and implement server-side checkout functionality efficiently. Ideal for React developers seeking practical, hands-on experience in web development."},{"slug":"context","fullSlug":"/lessons/core-react-concepts/context","title":"Context","order":"04G","path":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/04-core-react-concepts/G-context.md","description":"Learn how to manage global state in a React application by implementing a shopping cart using context. This tutorial, part of the Complete Intro to React v9 course by Brian Holt, guides you in creating reusable components and sharing state across the application without prop drilling, enhancing your React skills with practical examples."}],"order":"04"},{"icon":"seedling","title":"Ecosystem","slug":"ecosystem","lessons":[{"slug":"tanstack-router","fullSlug":"/lessons/ecosystem/tanstack-router","title":"TanStack Router","order":"05A","path":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/05-ecosystem/A-tanstack-router.md","description":"Discover how to implement routing in React using TanStack Router with insights from Brian Holt's Complete Intro to React v9. Learn about client-side focused routing solutions, file-based route generation, and lazy loading techniques to optimize your React applications."},{"slug":"tanstack-query","fullSlug":"/lessons/ecosystem/tanstack-query","title":"TanStack Query","order":"05B","path":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/05-ecosystem/B-tanstack-query.md","description":"Learn how to create a past orders page in React using TanStack Query to facilitate seamless asynchronous API calls, enhancing code cleanliness and efficiency. This guide by seasoned developer Brian Holt provides detailed instructions on integrating React Query ESLint configurations and leveraging React Query Devtools for better debugging. Ideal for web developers looking to optimize React-based projects with effective data fetching techniques."}],"order":"05"},{"icon":"dumbbell","title":"Advanced React","slug":"advanced-react","lessons":[{"slug":"portals","fullSlug":"/lessons/advanced-react/portals","title":"Portals","order":"06A","path":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/06-advanced-react/A-portals.md","description":"Learn how to implement a React modal using portals for rendering components outside the main app structure in Brian Holt's \"Complete Intro to React.\" This guide covers use of React Query for asynchronous data management and demonstrates best practices in creating user interface elements like modals to improve web development skills. Ideal for React developers seeking practical experience with advanced rendering techniques and API integration."},{"slug":"error-boundaries","fullSlug":"/lessons/advanced-react/error-boundaries","title":"Error Boundaries","order":"06B","path":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/06-advanced-react/B-error-boundaries.md","description":"Learn how to use class components and error boundaries in React development with Brian Holt's \"Complete Intro to React, v9\" course. Understand the transition from function components to class components for error handling and gain insights into managing API errors using techniques like `componentDidCatch`. Ideal for developers navigating legacy React codebases or dealing with API errors, this course offers practical skills to improve your React applications."},{"slug":"uncontrolled-forms","fullSlug":"/lessons/advanced-react/uncontrolled-forms","title":"Uncontrolled Forms","order":"06C","path":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/06-advanced-react/C-uncontrolled-forms.md","description":"Learn how to create a contact page using TanStack Query in React with Brian Holt's Complete Intro to React, v9. Discover how to handle posts via mutations, manage uncontrolled forms, and integrate these features seamlessly into your React application."}],"order":"06"},{"icon":"vial","title":"Testing","slug":"testing","lessons":[{"slug":"vitest","fullSlug":"/lessons/testing/vitest","title":"Vitest","order":"07A","path":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/07-testing/A-vitest.md","description":"Learn how to set up and perform testing on React applications using Vitest, a test runner compatible with Vite's build pipeline, from Brian Holt's Complete Intro to React course. This guide provides step-by-step instructions for integrating Vitest with @testing-library/react for efficient React testing, drawing on similarities with Jasmine and Jest APIs to deliver seamless testing experiences. Ideal for React developers seeking efficient test integration, this tutorial emphasizes real-time feedback and browser-like test environments with happy-dom."},{"slug":"basic-react-tests","fullSlug":"/lessons/testing/basic-react-tests","title":"Basic React Tests","order":"07B","path":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/07-testing/B-basic-react-tests.md","description":"Learn how to effectively test React components with a focus on functionality over implementation using React Testing Library, through this hands-on guide by Brian Holt. Gain insights into user-centric testing methodologies, writing bug-catching tests, and managing test lifecycle for enhanced app reliability, specifically applied to the Pizza.jsx component."},{"slug":"testing-user-interaction","fullSlug":"/lessons/testing/testing-user-interaction","title":"Testing User Interaction","order":"07C","path":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/07-testing/C-testing-user-interaction.md","description":"Learn how to test a Contact page in React using Vitest and vitest-fetch-mock, a tool to simplify fetch mocking. The tutorial covers setting up mocks, rendering components with React Query's QueryClientProvider, filling out form inputs, simulating user interactions, and verifying API requests, providing a comprehensive guide for beginner to job-ready React developers under guidance from Brian Holt, creator of the Complete Intro to React course, version 9."},{"slug":"testing-custom-hooks","fullSlug":"/lessons/testing/testing-custom-hooks","title":"Testing Custom Hooks","order":"07D","path":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/07-testing/D-testing-custom-hooks.md","description":"Learn how to test a custom React hook, usePizzaOfTheDay, by implementing a fake component and utilizing renderHook from testing-library/react. This approach ensures your hooks behave correctly, maintaining code integrity while interacting with APIs, like the example API call for \"Pizza of the Day.\""},{"slug":"snapshot-testing","fullSlug":"/lessons/testing/snapshot-testing","title":"Snapshot Testing","order":"07E","path":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/07-testing/E-snapshot-testing.md","description":"Discover snapshot testing in React with Brian Holt's Complete Intro to React, a course offering insights into efficient testing techniques for components like Cart.jsx. Learn about the benefits and limitations of snapshot tests, how to implement them using tools like Vitest, and explore their practical applications in UI and API development. Ideal for developers looking to enhance their testing strategies in web development."},{"slug":"coverage","fullSlug":"/lessons/testing/coverage","title":"v8","order":"07F","path":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/07-testing/F-coverage.md","description":"Learn how to enhance your React test coverage with Vitest and v8 in the Complete Intro to React v9 course by Brian Holt. This guide explains setting up Chrome's built-in test coverage tool, v8, to visualize code coverage and improve your test effectiveness, with alternative support for Istanbul. Perfect for React developers aiming to optimize their testing strategies."},{"slug":"vitest-ui","fullSlug":"/lessons/testing/vitest-ui","title":"Vitest UI","order":"07G","path":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/07-testing/G-vitest-ui.md","description":"Learn how to use Vitest UI, an efficient tool for managing tests in your browser, by adding it to your project with simple npm commands. Enhance your React development skills by integrating browser-based testing with Vitest for convenient module graph visualization and individual test management. Ideal for developers seeking practical tools within the Complete Intro to React course by Brian Holt."},{"slug":"browser-tests","fullSlug":"/lessons/testing/browser-tests","title":"Browser Tests","order":"07H","path":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/07-testing/H-browser-tests.md","description":"Explore the emerging support for browser-based testing with Vitest, featuring Playwright integration for fast and reliable testing. Learn how to configure Vitest for both Node.js and browser environments, leveraging libraries like vitest-browser-react, and gain insights from Brian Holt on crafting effective tests for user experiences in React applications. This guide is ideal for developers keen on improving their testing setups in modern web development environments."}],"order":"07"},{"icon":"shuttle-space","title":"Whats Next","slug":"whats-next","lessons":[{"slug":"react-19","fullSlug":"/lessons/whats-next/react-19","title":"React 19","order":"08A","path":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/08-whats-next/A-react-19.md","description":"Explore the Complete Intro to React (v9) course by Brian Holt, focusing on core React concepts and upcoming React 19 features like 'use client' and 'use server'. Gain insights into new capabilities such as putting elements in the head, preload/preconnect hints, and seamless integration of custom components in React development."},{"slug":"form-actions","fullSlug":"/lessons/whats-next/form-actions","title":"Form Actions","order":"08B","path":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/08-whats-next/B-form-actions.md","description":"Learn how to upgrade your application to React 19 and simplify form handling in this segment of Brian Holt’s Complete Intro to React, version 9. Discover new React form actions, \"use server\" directives for server-side logic, and useFormData hooks to manage input states effectively. Ideal for React developers seeking practical techniques to streamline their web applications."},{"slug":"use-and-suspense","fullSlug":"/lessons/whats-next/use-and-suspense","title":"use and Suspense","order":"08C","path":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/08-whats-next/C-use-and-suspense.md","description":"Explore React Suspense and the new 'use' hook in React 19, alongside TanStack Query integration, to manage component rendering and data loading seamlessly. Learn how to implement Suspense to improve code readability and handling of async operations in React applications, guided by seasoned developer Brian Holt."},{"slug":"react-compiler","fullSlug":"/lessons/whats-next/react-compiler","title":"React Compiler","order":"08D","path":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/08-whats-next/D-react-compiler.md","description":"Learn about performance optimization in React with tools like useMemo and useCallback, and discover how the React Compiler can automate these optimizations for you. This guide provides practical steps for integrating React Compiler into your development workflow, ensuring improved performance with minimal effort."}],"order":"08"},{"icon":"hands-clapping","title":"Wrap Up","slug":"wrap-up","lessons":[{"slug":"congrats","fullSlug":"/lessons/wrap-up/congrats","title":"Congrats","order":"09A","path":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/09-wrap-up/A-congrats.md","description":"Congratulations on completing the Complete Intro to React v9 course by Brian Holt! You're ready to develop React applications professionally, with skills applicable to real-world tech jobs. Consider advancing your knowledge with Intermediate React v5, exploring server-side React techniques, or diving into hands-on projects to solidify your understanding."}],"order":"09"}]},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/advanced-react/error-boundaries.json b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/advanced-react/error-boundaries.json new file mode 100644 index 0000000..19b7899 --- /dev/null +++ b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/advanced-react/error-boundaries.json @@ -0,0 +1 @@ +{"pageProps":{"post":{"attributes":{"description":"Learn how to use class components and error boundaries in React development with Brian Holt's \"Complete Intro to React, v9\" course. Understand the transition from function components to class components for error handling and gain insights into managing API errors using techniques like `componentDidCatch`. Ideal for developers navigating legacy React codebases or dealing with API errors, this course offers practical skills to improve your React applications.","keywords":["React error boundaries","class components","API error handling","React development","Brian Holt","legacy React","Complete Intro to React"]},"html":"

Error Boundaries

\n

Frequently there are 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.

\n

This will also catch 404s on our API if someone give it an invalid ID!

\n

A component can only catch errors in its children, so that's important to keep in mind. It cannot catch its own errors. Let's go make a wrapper to use on PastOrdersRoute. Make a new file called ErrorBoundary.jsx

\n

This is also the first time I'm going to be showing you what was the old way of writing components, called class components. All React code used to be written this way but while it's not deprecated it is explicitly not recommended by the React team. Previous to this version of the Complete Intro to React I still taught class components as part of the course. In the past year, I kept track and I wrote class components zero times. And as such it is time to retire teaching them as part of an intro course. However, with error boundaries it is the only way to write them, so we will do a brief intro to them so we can learn them. In reality, I would likely just use react-error-boundary and never have to write a class component at all!

\n

A class component is really similar to a function component (which is what we have been writing) but have a few key differences.

\n\n
import { Component } from "react";\nimport { Link } from "@tanstack/react-router";\n\nclass ErrorBoundary extends Component {\n  state = { hasError: false };\n  static getDerivedStateFromError() {\n    return { hasError: true };\n  }\n  componentDidCatch(error, info) {\n    console.error("ErrorBoundary caught an error", error, info);\n  }\n  render() {\n    if (this.state.hasError) {\n      return (\n        <div className="error-boundary">\n          <h2>Uh oh!</h2>\n          <p>\n            There was an error with this listing. <Link to="/">Click here</Link>{" "}\n            to back to the home page.\n          </p>\n        </div>\n      );\n    }\n\n    return this.props.children;\n  }\n}\n\nexport default ErrorBoundary;\n
\n

Let's go make PastOrderRoute use it in case the API throws an error.

\n
// add import\nimport ErrorBoundary from "../ErrorBoundary";\n\n// replace Route export\nexport const Route = createLazyFileRoute("/past")({\n  component: ErrorBoundaryWrappedPastOrderRoutes,\n});\n\n// beneath Route export\nfunction ErrorBoundaryWrappedPastOrderRoutes() {\n  return (\n    <ErrorBoundary>\n      <PastOrdersRoute />\n    </ErrorBoundary>\n  );\n}\n
\n

Now, if you want to, go add a throw new Error("lol"); in your render function for the PastOrderRoute to see it work!

\n
\n

🏁 Click here to see the state of the project up until now: 12-error-boundaries

\n
\n","markdown":"\n## Error Boundaries\n\nFrequently there are 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.\n\nThis will also catch 404s on our API if someone give it an invalid ID!\n\nA component can only catch errors in its children, so that's important to keep in mind. It cannot catch its own errors. Let's go make a wrapper to use on PastOrdersRoute. Make a new file called ErrorBoundary.jsx\n\nThis is also the first time I'm going to be showing you what was the old way of writing components, called class components. All React code used to be written this way but while it's not deprecated it is explicitly not recommended by the React team. Previous to this version of the Complete Intro to React I still taught class components as part of the course. In the past year, I kept track and I wrote class components _zero_ times. And as such it is time to retire teaching them as part of an intro course. However, with error boundaries it is the only way to write them, so we will do a brief intro to them so we can learn them. In reality, I would likely just use [react-error-boundary][reb] and never have to write a class component at all!\n\nA class component is really similar to a function component (which is what we have been writing) but have a few key differences.\n\n- You cannot use any hooks with class components (useState, useEffect, etc.)\n- Every class component has a `render()` function. It works mostly the same as a function component.\n- Instead of the useState hook you'll use `this.state` which is an object that contains all state for that component.\n- To change state, you'll use a `this.setState({ key: \"value\" })` function. This allows you to change state like your `setKey` hook would have.\n- Instead of useEffect, you use lifecycle methods. componentDidMount, componentDidUpdate, componentWillUnmount, etc. To read more about them, [go here][lifecycle].\n- Instead of a props parameter being passed into the function, you'll use `this.props`.\n- This is enough here. You shouldn't be authoring class components anymore except for error boundaries. But you may encounter them in legacy React codebases.\n\n```javascript\nimport { Component } from \"react\";\nimport { Link } from \"@tanstack/react-router\";\n\nclass ErrorBoundary extends Component {\n state = { hasError: false };\n static getDerivedStateFromError() {\n return { hasError: true };\n }\n componentDidCatch(error, info) {\n console.error(\"ErrorBoundary caught an error\", error, info);\n }\n render() {\n if (this.state.hasError) {\n return (\n
\n

Uh oh!

\n

\n There was an error with this listing. Click here{\" \"}\n to back to the home page.\n

\n
\n );\n }\n\n return this.props.children;\n }\n}\n\nexport default ErrorBoundary;\n```\n\n- Now anything that is a child of this component will have errors caught here. Think of this like a catch block from try/catch.\n- A static method is one that can be called on the constructor. You'd call this method like this: `ErrorBoundary.getDerivedStateFromError(error)`. This method must be static.\n- If you want to call an error logging service, `componentDidCatch` would be an amazing place to do that. I can recommend [Sentry][sentry] and [TrackJS][trackjs].\n- `this.props.children` is any child component inside of the component. e.g. if someone renders `

hi

`, the `

hi

` is consider the the \"children\". This works in function components too.\n- Because we just return children if there's no error, the ErrorBoundary component doesn't render anything itself if there are no errors.\n\nLet's go make PastOrderRoute use it in case the API throws an error.\n\n```javascript\n// add import\nimport ErrorBoundary from \"../ErrorBoundary\";\n\n// replace Route export\nexport const Route = createLazyFileRoute(\"/past\")({\n component: ErrorBoundaryWrappedPastOrderRoutes,\n});\n\n// beneath Route export\nfunction ErrorBoundaryWrappedPastOrderRoutes() {\n return (\n \n \n \n );\n}\n```\n\n- Now this is totally self contained. No one rendering PastOrderRoute has to know that it has its own error boundary. I'll let you decide if you like this pattern or if you would have preferred doing this in App.js at the Router level. Differing opinions exist.\n- We totally could have made ErrorBoundary a bit more flexible and made it able to accept a component to display in cases of errors. In general I recommend the \"WET\" code rule (as opposed to [DRY][dry], lol): Write Everything Twice (or I even prefer Write Everything Thrice). In this case, we have one use case for this component, so I won't spend the extra time to make it flexible. If I used it again, I'd make it work for both of those use cases, but not _every_ use case. On the third or fourth time, I'd then go back and invest the time to make it flexible.\n\nNow, if you want to, go add a `throw new Error(\"lol\");` in your render function for the PastOrderRoute to see it work!\n\n> 🏁 [Click here to see the state of the project up until now: 12-error-boundaries][step]\n\n[step]: https://github.com/btholt/citr-v9-project/tree/master/12-error-boundaries\n[sentry]: https://sentry.io/\n[trackjs]: https://trackjs.com/\n[dry]: https://en.wikipedia.org/wiki/Don%27t_repeat_yourself\n[reb]: https://github.com/bvaughn/react-error-boundary\n[lifecycle]: https://react.dev/reference/react/Component\n","slug":"error-boundaries","title":"Error Boundaries","section":"Advanced React","icon":"dumbbell","filePath":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/06-advanced-react/B-error-boundaries.md","nextSlug":"/lessons/advanced-react/uncontrolled-forms","prevSlug":"/lessons/advanced-react/portals"}},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/advanced-react/portals.json b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/advanced-react/portals.json new file mode 100644 index 0000000..516ee11 --- /dev/null +++ b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/advanced-react/portals.json @@ -0,0 +1 @@ +{"pageProps":{"post":{"attributes":{"description":"Learn how to implement a React modal using portals for rendering components outside the main app structure in Brian Holt's \"Complete Intro to React.\" This guide covers use of React Query for asynchronous data management and demonstrates best practices in creating user interface elements like modals to improve web development skills. Ideal for React developers seeking practical experience with advanced rendering techniques and API integration.","keywords":["React modals","portals","React Query","web development","Brian Holt"]},"html":"

Portals

\n

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.

\n

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.

\n
// above #root\n<div id="modal"></div>\n

By default this will have nothing in it, but once we render our modal it render inside this div instead of root. Make a new file in src called Modal.jsx

\n
// basically stolen from the React docs\nimport { useEffect, useRef } from "react";\nimport { createPortal } from "react-dom";\n\nconst Modal = ({ children }) => {\n  const elRef = useRef(null);\n  if (!elRef.current) {\n    elRef.current = document.createElement("div");\n  }\n\n  useEffect(() => {\n    const modalRoot = document.getElementById("modal");\n    modalRoot.appendChild(elRef.current);\n    return () => modalRoot.removeChild(elRef.current);\n  }, []);\n\n  return createPortal(<div>{children}</div>, elRef.current);\n};\n\nexport default Modal;\n
\n

Before we use our Modal, we'll need to make another API call function get a past order. In our example here, we're going to make it so you can click on one of the rows in our past orders page to see what was in the order. In src/api, add a file called getPastOrder.js (no s) and add this.

\n
export default async function getPastOrder(order) {\n  const response = await fetch(`/api/past-order/${order}`);\n  const data = await response.json();\n  return data;\n}\n

Cool, let's go use this to render our Modal now. Open past.lazy.jsx. Make the order.order_id value a button and add an onClick event so clicking it will open the Modal:

\n
<td>\n  <button onClick={() => setFocusedOrder(order.order_id)}>\n    {order.order_id}\n  </button>\n</td>\n<td>{order.date}</td>\n<td>{order.time}</td>\n

Then render the modal if we have a focusedOrder:

\n
// import at top\nimport getPastOrder from "../api/getPastOrder";\nimport Modal from "../Modal";\n\nconst intl = new Intl.NumberFormat("en-US", {\n  style: "currency",\n  currency: "USD",\n});\n\n// top of the render function\nconst [focusedOrder, setFocusedOrder] = useState();\n\nconst { isLoading: isLoadingPastOrder, data: pastOrderData } = useQuery({\n  queryKey: ["past-order", focusedOrder],\n  queryFn: () => getPastOrder(focusedOrder),\n  enabled: !!focusedOrder,\n  staleTime: 24 * 60 * 60 * 1000, // one day in milliseconds,\n});\n\n// last thing before closing div\n{\n  focusedOrder ? (\n    <Modal>\n      <h2>Order #{focusedOrder}</h2>\n      {!isLoadingPastOrder ? (\n        <table>\n          <thead>\n            <tr>\n              <td>Image</td>\n              <td>Name</td>\n              <td>Size</td>\n              <td>Quantity</td>\n              <td>Price</td>\n              <td>Total</td>\n            </tr>\n          </thead>\n          <tbody>\n            {pastOrderData.orderItems.map((pizza) => (\n              <tr key={`${pizza.pizzaTypeId}_${pizza.size}`}>\n                <td>\n                  <img src={pizza.image} alt={pizza.name} />\n                </td>\n                <td>{pizza.name}</td>\n                <td>{pizza.size}</td>\n                <td>{pizza.quantity}</td>\n                <td>{intl.format(pizza.price)}</td>\n                <td>{intl.format(pizza.total)}</td>\n              </tr>\n            ))}\n          </tbody>\n        </table>\n      ) : (\n        <p>Loading …</p>\n      )}\n      <button onClick={() => setFocusedOrder()}>Close</button>\n    </Modal>\n  ) : null;\n}\n
\n

That's it!

\n
\n

🏁 Click here to see the state of the project up until now: 11-modals

\n
\n","markdown":"\n## Portals\n\nWhat 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.\n\nWe'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.\n\n```javascript\n// above #root\n
\n```\n\nBy default this will have nothing in it, but once we render our modal it render inside this div instead of root. Make a new file in src called Modal.jsx\n\n```javascript\n// basically stolen from the React docs\nimport { useEffect, useRef } from \"react\";\nimport { createPortal } from \"react-dom\";\n\nconst Modal = ({ children }) => {\n const elRef = useRef(null);\n if (!elRef.current) {\n elRef.current = document.createElement(\"div\");\n }\n\n useEffect(() => {\n const modalRoot = document.getElementById(\"modal\");\n modalRoot.appendChild(elRef.current);\n return () => modalRoot.removeChild(elRef.current);\n }, []);\n\n return createPortal(
{children}
, elRef.current);\n};\n\nexport default Modal;\n```\n\n- This uses a ref. A ref is a reference to something that need to be exactly the same between renders. A hook would get regenerated / recreated so we need a ref because it'll create a div and then it hand back the _same_ div every render. It's important that it's the same div because it'll be the one we use to render the portal.\n- We're using createPortal to render into this new modal div we're chosen for our modal. But this could be a contextual nav or any other DOM div we want.\n- We use the returned function on the effect to clean up when this Modal is unmounted from the DOM. Otherwise we'd leak memory.\n\nBefore we use our Modal, we'll need to make another API call function get a past order. In our example here, we're going to make it so you can click on one of the rows in our past orders page to see what was in the order. In `src/api`, add a file called `getPastOrder.js` (no `s`) and add this.\n\n```javascript\nexport default async function getPastOrder(order) {\n const response = await fetch(`/api/past-order/${order}`);\n const data = await response.json();\n return data;\n}\n```\n\nCool, let's go use this to render our Modal now. Open `past.lazy.jsx`. Make the `order.order_id` value a button and add an `onClick` event so clicking it will open the Modal:\n\n```javascript\n\n \n\n{order.date}\n{order.time}\n```\n\nThen render the modal if we have a `focusedOrder`: \n\n```javascript\n// import at top\nimport getPastOrder from \"../api/getPastOrder\";\nimport Modal from \"../Modal\";\n\nconst intl = new Intl.NumberFormat(\"en-US\", {\n style: \"currency\",\n currency: \"USD\",\n});\n\n// top of the render function\nconst [focusedOrder, setFocusedOrder] = useState();\n\nconst { isLoading: isLoadingPastOrder, data: pastOrderData } = useQuery({\n queryKey: [\"past-order\", focusedOrder],\n queryFn: () => getPastOrder(focusedOrder),\n enabled: !!focusedOrder,\n staleTime: 24 * 60 * 60 * 1000, // one day in milliseconds,\n});\n\n// last thing before closing div\n{\n focusedOrder ? (\n \n

Order #{focusedOrder}

\n {!isLoadingPastOrder ? (\n \n \n \n \n \n \n \n \n \n \n \n \n {pastOrderData.orderItems.map((pizza) => (\n \n \n \n \n \n \n \n \n ))}\n \n
ImageNameSizeQuantityPriceTotal
\n {pizza.name}\n {pizza.name}{pizza.size}{pizza.quantity}{intl.format(pizza.price)}{intl.format(pizza.total)}
\n ) : (\n

Loading …

\n )}\n \n
\n ) : null;\n}\n```\n\n- We're using React Query again here. We're using the `enabled` key to only make this request when the user has set a focusedOrder. If there's no focusedOrder, then it won't make the request. `!!` is the same as \"not not\". It makes a number like `5` be `true` and a number like `0` or `undefined` be `false`.\n- We set the staleTime to day. These orders shouldn't change very frequently.\n- If there's no focusedOrder, we don't render the Modal, which means the portal is unused.\n- If there is a focusedOrder, we render the modal and show a loading indicator that we're loading the rest of the info.\n- When a user clicks Close, we set the focusedOrder to be undefined again which causes the Modal to unrender.\n\nThat's it!\n\n> 🏁 [Click here to see the state of the project up until now: 11-modals][step]\n\n[step]: https://github.com/btholt/citr-v9-project/tree/master/11-modals\n","slug":"portals","title":"Portals","section":"Advanced React","icon":"dumbbell","filePath":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/06-advanced-react/A-portals.md","nextSlug":"/lessons/advanced-react/error-boundaries","prevSlug":"/lessons/ecosystem/tanstack-query"}},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/advanced-react/uncontrolled-forms.json b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/advanced-react/uncontrolled-forms.json new file mode 100644 index 0000000..2fb1b10 --- /dev/null +++ b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/advanced-react/uncontrolled-forms.json @@ -0,0 +1 @@ +{"pageProps":{"post":{"attributes":{"description":"Learn how to create a contact page using TanStack Query in React with Brian Holt's Complete Intro to React, v9. Discover how to handle posts via mutations, manage uncontrolled forms, and integrate these features seamlessly into your React application.","keywords":["React contact page","TanStack Query","uncontrolled forms","React","Brian Holt"]},"html":"

Uncontrolled Forms

\n

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.

\n

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.

\n
export default async function postContact(name, email, message) {\n  const response = await fetch("/api/contact", {\n    method: "POST",\n    headers: {\n      "Content-Type": "application/json",\n    },\n    body: JSON.stringify({ name, email, message }),\n  });\n\n  if (!response.ok) {\n    throw new Error("Network response was not ok");\n  }\n\n  return response.json();\n}\n

Create a new link to our page in index.lazy.jsx

\n
// after past orders\n<li>\n  <Link to="/contact">Contact</Link>\n</li>\n

Create a new route called contact.lazy.jsx in src/routes

\n
import { createLazyFileRoute } from "@tanstack/react-router";\nimport { useMutation } from "@tanstack/react-query";\nimport postContact from "../api/postContact";\n\nexport const Route = createLazyFileRoute("/contact")({\n  component: ContactRoute,\n});\n\nfunction ContactRoute() {\n  const mutation = useMutation({\n    mutationFn: function (e) {\n      e.preventDefault();\n      const formData = new FormData(e.target);\n      return postContact(\n        formData.get("name"),\n        formData.get("email"),\n        formData.get("message")\n      );\n    },\n  });\n\n  return (\n    <div className="contact">\n      <h2>Contact</h2>\n      {mutation.isSuccess ? (\n        <h3>Submitted!</h3>\n      ) : (\n        <form onSubmit={mutation.mutate}>\n          <input name="name" placeholder="Name" />\n          <input type="email" name="email" placeholder="Email" />\n          <textarea placeholder="Message" name="message"></textarea>\n          <button>Submit</button>\n        </form>\n      )}\n    </div>\n  );\n}\n
\n

Try it! You'll notice in the logs of where-ever your API is running that it logs out the contact info.

\n
\n

🏁 Click here to see the state of the project up until now: 13-uncontrolled-forms

\n
\n","markdown":"\n## Uncontrolled Forms\n\nWe 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.\n\nWe 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.\n\n```javascript\nexport default async function postContact(name, email, message) {\n const response = await fetch(\"/api/contact\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ name, email, message }),\n });\n\n if (!response.ok) {\n throw new Error(\"Network response was not ok\");\n }\n\n return response.json();\n}\n```\n\nCreate a new link to our page in index.lazy.jsx\n\n```javascript\n// after past orders\n
  • \n Contact\n
  • \n```\n\nCreate a new route called contact.lazy.jsx in src/routes\n\n```javascript\nimport { createLazyFileRoute } from \"@tanstack/react-router\";\nimport { useMutation } from \"@tanstack/react-query\";\nimport postContact from \"../api/postContact\";\n\nexport const Route = createLazyFileRoute(\"/contact\")({\n component: ContactRoute,\n});\n\nfunction ContactRoute() {\n const mutation = useMutation({\n mutationFn: function (e) {\n e.preventDefault();\n const formData = new FormData(e.target);\n return postContact(\n formData.get(\"name\"),\n formData.get(\"email\"),\n formData.get(\"message\")\n );\n },\n });\n\n return (\n
    \n

    Contact

    \n {mutation.isSuccess ? (\n

    Submitted!

    \n ) : (\n
    \n \n \n \n \n
    \n )}\n
    \n );\n}\n```\n\n- We are using a mutation from TanStack React Query. It works very similar to normal gets except this one sends information to the API instead of gets it.\n- Notice we're using `isSuccess` to show or hide the form. Once the mutation has successfully been submitted, we just want to show them that and then hide the form.\n- Notice we're not using state or useState at all here. We're using an \"uncontrolled\" form which just means we're letting the DOM manage the state. Only on a submit event are we reading from the DOM using the FormData API. We use that to submit to our API the contact data.\n- There's also isError and isIdle, we just didn't use them from TanStack Query.\n- Also notice in the dev tools for TanStack Query the mutations (it's in another tab.)\n\nTry it! You'll notice in the logs of where-ever your API is running that it logs out the contact info.\n\n> 🏁 [Click here to see the state of the project up until now: 13-uncontrolled-forms][step]\n\n[step]: https://github.com/btholt/citr-v9-project/tree/master/13-uncontrolled-forms\n","slug":"uncontrolled-forms","title":"Uncontrolled Forms","section":"Advanced React","icon":"dumbbell","filePath":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/06-advanced-react/C-uncontrolled-forms.md","nextSlug":"/lessons/testing/vitest","prevSlug":"/lessons/advanced-react/error-boundaries"}},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/core-react-concepts/context.json b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/core-react-concepts/context.json new file mode 100644 index 0000000..ce90230 --- /dev/null +++ b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/core-react-concepts/context.json @@ -0,0 +1 @@ +{"pageProps":{"post":{"attributes":{"description":"Learn how to manage global state in a React application by implementing a shopping cart using context. This tutorial, part of the Complete Intro to React v9 course by Brian Holt, guides you in creating reusable components and sharing state across the application without prop drilling, enhancing your React skills with practical examples.","keywords":["React","shopping cart","context API","global state","Brian Holt"]},"html":"

    Context

    \n

    Let's make a cart indicator on the top right of the page. Create a file called Header.jsx and put this in there.

    \n
    export default function Header() {\n  return (\n    <nav>\n      <h1 className="logo">Padre Gino's Pizza</h1>\n      <div className="nav-cart">\n        🛒<span className="nav-cart-number">5</span>\n      </div>\n    </nav>\n  );\n}\n

    Now let's use that in App.jsx

    \n
    // at top\nimport Header from "./Header";\n\n// replace .logo\n<Header />\n

    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.

    \n

    Context is for app-level state. This is state that exists for your entire app. The currently logged in user would be a good example of this. You wouldn't want the user to exist in just the page level because once you navigate to another page, all state of the previous page is destroyed. You'd want that user info to persist between pages, and thus context is a good thing for that. Context allows you to keep a global state for your app. We're going to use it for the shopping cart which actually makes good sense: if a user navigates to another page, you'd want to keep their shopping cart between loads. This is a good way to think about when you may want to use context: if you want that stage to persist between pages.

    \n

    So let's make it work. Make a file called contexts.jsx. It's not a component so I tend to not capitalize it. The React docs do capitalize it. Up to you.

    \n
    import { createContext } from "react";\n\nexport const CartContext = createContext([[], function () {}]);\n

    That's it. Pretty simple. It's called contexts with an s because if you had other contexts (like a UserContext) you could put those all in the same file. The [[], function () {}] isn't strictly necessary; it's a default value your context would use if no context provider is there (which should never happen.) This really only ends up being useful for TypeScript types – the type you give here is what TypeScript will use to validate it. In theory it could be helpful for testing too. The reason for the weird value is that it's a React hook: an array where the first value is an array (like our cart is) and the second value is a function (the setCart function).

    \n

    Okay, let's go put it in App.jsx.

    \n
    // at the top\nimport { StrictMode, useState } from "react"; // need useState\nimport { CartContext } from "./contexts";\n\n// replace App\nconst App = () => {\n  const cartHook = useState([]);\n  return (\n    <StrictMode>\n      <CartContext.Provider value={cartHook}>\n        <div>\n          <Header />\n          <Order />\n          <PizzaOfTheDay />\n        </div>\n      </CartContext.Provider>\n    </StrictMode>\n  );\n};\n

    We're creating a hook (notice we are not destructuring) and passing that in as the value. The Provider makes that context available only inside of its children. In theory you could have multiple providers from the same context but honestly I've never had a reason to do it.

    \n

    Alright, let's go make Order.jsx work now.

    \n
    // at top\nimport { useState, useEffect, useContext } from "react"; // need useContext\nimport { CartContext } from "./contexts";\n\nconst [cart, setCart] = useContext(CartContext); // change cart hook to use context\n

    That's it! Everything else works the same. In our case we're just using a hook directly from context, but you can put anything in context; it doesn't have to be a hook.

    \n

    Let's go use it in our Header now too.

    \n
    // at top\nimport { useContext } from "react";\nimport { CartContext } from "./contexts";\n\n// top of function\nconst [cart] = useContext(CartContext);\n\n// replace span number\n🛒<span className="nav-cart-number">{cart.length}</span>\n

    That's it! We don't need the setCart function so we don't import that. Otherwise all looks pretty normal!

    \n

    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 <Header cart={cart} /> 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.

    \n
    \n

    🏁 Click here to see the state of the project up until now: 08-context

    \n
    \n","markdown":"\n## Context\n\nLet's make a cart indicator on the top right of the page. Create a file called Header.jsx and put this in there.\n\n```javascript\nexport default function Header() {\n return (\n \n );\n}\n```\n\nNow let's use that in App.jsx\n\n```javascript\n// at top\nimport Header from \"./Header\";\n\n// replace .logo\n
    \n```\n\nRight 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.\n\nContext is for app-level state. This is state that exists for your entire app. The currently logged in user would be a good example of this. You wouldn't want the user to exist in just the page level because once you navigate to another page, all state of the previous page is destroyed. You'd want that user info to persist between pages, and thus context is a good thing for that. Context allows you to keep a global state for your app. We're going to use it for the shopping cart which actually makes good sense: if a user navigates to another page, you'd want to keep their shopping cart between loads. This is a good way to think about when you may want to use context: if you want that stage to persist between pages.\n\nSo let's make it work. Make a file called `contexts.jsx`. It's not a component so I tend to not capitalize it. The React docs do capitalize it. Up to you.\n\n```javascript\nimport { createContext } from \"react\";\n\nexport const CartContext = createContext([[], function () {}]);\n```\n\nThat's it. Pretty simple. It's called contexts with an `s` because if you had other contexts (like a UserContext) you could put those all in the same file. The `[[], function () {}]` isn't strictly necessary; it's a default value your context would use if no context provider is there (which should never happen.) This really only ends up being useful for TypeScript types – the type you give here is what TypeScript will use to validate it. In theory it could be helpful for testing too. The reason for the weird value is that it's a React hook: an array where the first value is an array (like our cart is) and the second value is a function (the setCart function).\n\nOkay, let's go put it in App.jsx.\n\n```javascript\n// at the top\nimport { StrictMode, useState } from \"react\"; // need useState\nimport { CartContext } from \"./contexts\";\n\n// replace App\nconst App = () => {\n const cartHook = useState([]);\n return (\n \n \n
    \n
    \n \n \n
    \n
    \n
    \n );\n};\n```\n\nWe're creating a hook (notice we are not destructuring) and passing that in as the value. The Provider makes that context available only inside of its children. In theory you could have multiple providers from the same context but honestly I've never had a reason to do it.\n\nAlright, let's go make Order.jsx work now.\n\n```javascript\n// at top\nimport { useState, useEffect, useContext } from \"react\"; // need useContext\nimport { CartContext } from \"./contexts\";\n\nconst [cart, setCart] = useContext(CartContext); // change cart hook to use context\n```\n\nThat's it! Everything else works the same. In our case we're just using a hook directly from context, but you can put anything in context; it doesn't have to be a hook.\n\nLet's go use it in our Header now too.\n\n```javascript\n// at top\nimport { useContext } from \"react\";\nimport { CartContext } from \"./contexts\";\n\n// top of function\nconst [cart] = useContext(CartContext);\n\n// replace span number\n🛒{cart.length}\n```\n\nThat's it! We don't need the setCart function so we don't import that. Otherwise all looks pretty normal!\n\nSo 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.\n\n> 🏁 [Click here to see the state of the project up until now: 08-context][step]\n\n[step]: https://github.com/btholt/citr-v9-project/tree/master/08-context\n","slug":"context","title":"Context","section":"Core React Concepts","icon":"book","filePath":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/04-core-react-concepts/G-context.md","nextSlug":"/lessons/ecosystem/tanstack-router","prevSlug":"/lessons/core-react-concepts/handling-user-inputs"}},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/core-react-concepts/custom-hooks.json b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/core-react-concepts/custom-hooks.json new file mode 100644 index 0000000..b908555 --- /dev/null +++ b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/core-react-concepts/custom-hooks.json @@ -0,0 +1 @@ +{"pageProps":{"post":{"attributes":{"description":"Learn about creating custom hooks in React with Brian Holt's Complete Intro to React, covering how to build and integrate a custom hook for fetching pizza of the day using React hooks for effective separation and testability. Brian demonstrates building a component and using debug tools to inspect React custom hooks efficiently.","keywords":["custom hooks","React hooks","Brian Holt","pizza of the day","debug value"]},"html":"

    Custom Hooks

    \n

    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.

    \n

    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.

    \n

    Make a file called PizzaOfTheDay.jsx

    \n
    import { usePizzaOfTheDay } from "./usePizzaOfTheDay";\n\n// feel free to change en-US / USD to your locale\nconst intl = new Intl.NumberFormat("en-US", {\n  style: "currency",\n  currency: "USD",\n});\n\nconst PizzaOfTheDay = () => {\n  const pizzaOfTheDay = usePizzaOfTheDay();\n\n  if (!pizzaOfTheDay) {\n    return <div>Loading...</div>;\n  }\n\n  return (\n    <div className="pizza-of-the-day">\n      <h2>Pizza of the Day</h2>\n      <div>\n        <div className="pizza-of-the-day-info">\n          <h3>{pizzaOfTheDay.name}</h3>\n          <p>{pizzaOfTheDay.description}</p>\n          <p className="pizza-of-the-day-price">\n            From: <span>{intl.format(pizzaOfTheDay.sizes.S)}</span>\n          </p>\n        </div>\n        <img\n          className="pizza-of-the-day-image"\n          src={pizzaOfTheDay.image}\n          alt={pizzaOfTheDay.name}\n        />\n      </div>\n    </div>\n  );\n};\n\nexport default PizzaOfTheDay;\n
    \n

    Okay, let's go make the hook! Make a file called usePizzaOfTheDay.jsx (you could call this .js instead of jsx but in a React project I just use JSX for all "React-y" things)

    \n
    import { useState, useEffect } from "react";\n\nexport const usePizzaOfTheDay = () => {\n  const [pizzaOfTheDay, setPizzaOfTheDay] = useState(null);\n\n  useEffect(() => {\n    async function fetchPizzaOfTheDay() {\n      const response = await fetch("/api/pizza-of-the-day");\n      const data = await response.json();\n      setPizzaOfTheDay(data);\n    }\n\n    fetchPizzaOfTheDay();\n  }, []);\n\n  return pizzaOfTheDay;\n};\n
    \n

    Lastly, let's go add this to App.jsx so our component renders.

    \n
    // import at top\nimport PizzaOfTheDay from "./PizzaOfTheDay";\n\n// under <Order />\n<PizzaOfTheDay />\n

    You should now see the new component which uses our new hook at the bottom!

    \n

    Let's add one more fun debugging technique made especially for custom hooks. Put this in usePizzaOfTheDay.jsx:

    \n
    // import useDebugValue\nimport { useState, useEffect, useDebugValue } from "react";\n\n// add under the hook being used in the render function\nuseDebugValue(pizzaOfTheDay ? `${pizzaOfTheDay.name}` : "Loading...");\n

    Now open your React Dev Tools and inspect our PizzaOfTheDay component. You'll see our debug value there. This is helpful when you have lots of custom hooks and in particular lots of reused custom hooks that have differing values. It can help at a glance to discern which hook has which data inside of it.

    \n
    \n

    🏁 Click here to see the state of the project up until now: 06-custom-hooks

    \n
    \n","markdown":"\n## Custom Hooks\n\nOne 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.\n\nOkay, 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.\n\nMake a file called PizzaOfTheDay.jsx\n\n```javascript\nimport { usePizzaOfTheDay } from \"./usePizzaOfTheDay\";\n\n// feel free to change en-US / USD to your locale\nconst intl = new Intl.NumberFormat(\"en-US\", {\n style: \"currency\",\n currency: \"USD\",\n});\n\nconst PizzaOfTheDay = () => {\n const pizzaOfTheDay = usePizzaOfTheDay();\n\n if (!pizzaOfTheDay) {\n return
    Loading...
    ;\n }\n\n return (\n
    \n

    Pizza of the Day

    \n
    \n
    \n

    {pizzaOfTheDay.name}

    \n

    {pizzaOfTheDay.description}

    \n

    \n From: {intl.format(pizzaOfTheDay.sizes.S)}\n

    \n
    \n \n
    \n
    \n );\n};\n\nexport default PizzaOfTheDay;\n```\n\n- The cool part here is the `usePizzaOfTheDay()`. We now just get to rely on that this going to provide us with the pizza of the day from within the black box of the hook working.\n\nOkay, let's go make the hook! Make a file called usePizzaOfTheDay.jsx (you could call this .js instead of jsx but in a React project I just use JSX for all \"React-y\" things)\n\n```javascript\nimport { useState, useEffect } from \"react\";\n\nexport const usePizzaOfTheDay = () => {\n const [pizzaOfTheDay, setPizzaOfTheDay] = useState(null);\n\n useEffect(() => {\n async function fetchPizzaOfTheDay() {\n const response = await fetch(\"/api/pizza-of-the-day\");\n const data = await response.json();\n setPizzaOfTheDay(data);\n }\n\n fetchPizzaOfTheDay();\n }, []);\n\n return pizzaOfTheDay;\n};\n```\n\n- This looks like what you'd see at the top of a component, right? Now it's just encapsulated as a hook which makes it easy to test by itself! 🎉\n- We can use `useState`, `useEffect`, or any hook we want here! We can even use other custom hooks!\n\nLastly, let's go add this to App.jsx so our component renders.\n\n```javascript\n// import at top\nimport PizzaOfTheDay from \"./PizzaOfTheDay\";\n\n// under \n\n```\n\nYou should now see the new component which uses our new hook at the bottom!\n\nLet's add one more fun debugging technique made especially for custom hooks. Put this in usePizzaOfTheDay.jsx:\n\n```javascript\n// import useDebugValue\nimport { useState, useEffect, useDebugValue } from \"react\";\n\n// add under the hook being used in the render function\nuseDebugValue(pizzaOfTheDay ? `${pizzaOfTheDay.name}` : \"Loading...\");\n```\n\nNow open your React Dev Tools and inspect our PizzaOfTheDay component. You'll see our debug value there. This is helpful when you have _lots_ of custom hooks and in particular lots of reused custom hooks that have differing values. It can help at a glance to discern which hook has which data inside of it.\n\n> 🏁 [Click here to see the state of the project up until now: 06-custom-hooks][step]\n\n[step]: https://github.com/btholt/citr-v9-project/tree/master/06-custom-hooks\n","slug":"custom-hooks","title":"Custom Hooks","section":"Core React Concepts","icon":"book","filePath":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/04-core-react-concepts/E-custom-hooks.md","nextSlug":"/lessons/core-react-concepts/handling-user-inputs","prevSlug":"/lessons/core-react-concepts/dev-tools"}},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/core-react-concepts/dev-tools.json b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/core-react-concepts/dev-tools.json new file mode 100644 index 0000000..361386b --- /dev/null +++ b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/core-react-concepts/dev-tools.json @@ -0,0 +1 @@ +{"pageProps":{"post":{"attributes":{"description":"Explore essential React development tools including NODE_ENV, Strict Mode, and React Dev Tools to enhance debugging, optimize performance, and maintain modern coding practices. Brian Holt's Complete Intro to React v9 offers practical insights into these tools for effective frontend development with Vite.js.","keywords":["React tools","NODE_ENV","Strict Mode","React Dev Tools","Brian Holt"]},"html":"

    React has some really great tools to enhance your developer experience. We'll go over a few of them here.

    \n

    NODE_ENV=development

    \n

    React already has a lot of developer conveniences built into it out of the box. What's better is that they automatically strip it out when you compile your code for production.

    \n

    So how do you get the debugging conveniences then? Well, if you're using Vite.js, it will compile your development server with an environment variable of NODE_ENV=development and then when you run vite build it will automatically change that to NODE_ENV=production which is how all the extra weight gets stripped out.

    \n

    Why is it important that we strip the debug stuff out? The dev bundle of React is quite a bit bigger and quite a bit slower than the production build. Make sure you're compiling with the correct environmental variables or your users will suffer.

    \n

    Strict Mode

    \n

    React has a new strict mode. If you wrap your app in <StrictMode></StrictMode> 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.

    \n

    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.

    \n

    Dev Tools

    \n

    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 for links.

    \n","markdown":"\nReact has some really great tools to enhance your developer experience. We'll go over a few of them here.\n\n## `NODE_ENV=development`\n\nReact already has a lot of developer conveniences built into it out of the box. What's better is that they automatically strip it out when you compile your code for production.\n\nSo how do you get the debugging conveniences then? Well, if you're using Vite.js, it will compile your development server with an environment variable of `NODE_ENV=development` and then when you run `vite build` it will automatically change that to `NODE_ENV=production` which is how all the extra weight gets stripped out.\n\nWhy is it important that we strip the debug stuff out? The dev bundle of React is quite a bit bigger and quite a bit slower than the production build. Make sure you're compiling with the correct environmental variables or your users will suffer.\n\n## Strict Mode\n\nReact 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.\n\nBe 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.\n\n## Dev Tools\n\nReact 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.\n\n[dev-tools]: https://react.dev/learn/react-developer-tools\n","slug":"dev-tools","title":"Dev Tools","section":"Core React Concepts","icon":"book","filePath":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/04-core-react-concepts/D-dev-tools.md","nextSlug":"/lessons/core-react-concepts/custom-hooks","prevSlug":"/lessons/core-react-concepts/effects"}},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/core-react-concepts/effects.json b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/core-react-concepts/effects.json new file mode 100644 index 0000000..eb37a54 --- /dev/null +++ b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/core-react-concepts/effects.json @@ -0,0 +1 @@ +{"pageProps":{"post":{"attributes":{"description":"In this section of the Complete Intro to React v9 course by Brian Holt, learn how to use the `useEffect` hook to make API requests for initializing a list of pizzas, manage UI initial load with loading states, and optimize component updates using unique keys for rendering efficiency. Enhance your React projects with best practices for asynchronous data fetching and component optimization.","keywords":["React useEffect","API requests","React loading state","React component optimization","Brian Holt"]},"html":"

    Effects

    \n

    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.

    \n
    \n

    Make sure you have both your Vite dev server running and your API server running. Both.

    \n
    \n

    Let's refactor Order.jsx

    \n
    // change import at top\nimport { useEffect, useState } from "react";\nimport Pizza from "./Pizza";\n\n// outside of the render function\n// feel free to change en-US / USD to your locale\nconst intl = new Intl.NumberFormat("en-US", {\n  style: "currency",\n  currency: "USD",\n});\n\n// add to the other useStates inside component at top\nconst [pizzaTypes, setPizzaTypes] = useState([]);\nconst [loading, setLoading] = useState(true);\n\nlet price, selectedPizza;\nif (!loading) {\n  selectedPizza = pizzaTypes.find((pizza) => pizzaType === pizza.id);\n  price = intl.format(\n    selectedPizza.sizes ? selectedPizza.sizes[pizzaSize] : ""\n  );\n}\n\nuseEffect(() => {\n  fetchPizzaTypes();\n}, []);\n\nasync function fetchPizzaTypes() {\n  await new Promise((resolve) => setTimeout(resolve, 3000)); // remove this later, just to show you the loading state\n\n  const pizzasRes = await fetch("/api/pizzas");\n  const pizzasJson = await pizzasRes.json();\n  setPizzaTypes(pizzasJson);\n  setLoading(false);\n}\n\n// replace the options\n{\n  pizzaTypes.map((pizza) => (\n    <option key={pizza.id} value={pizza.id}>\n      {pizza.name}\n    </option>\n  ));\n}\n\n// replace <Pizza /> and button at the end\n
      \n
    • 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.
    • \n
    • 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.
    • \n
    • 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.
    • \n
    • 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.
    • \n
    • 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.
    • \n
    \n

    Updating the Selected Pizza & Price

    \n

    When a pizza is selected, we need to update the selected pizza and price. First, let's format the price after the selectedPizza state is updated:

    \n
      if(!loading){\n    selectedPizza = pizzaTypes.find((pizza) => pizzaType === pizza.id)\n    price = intl.format(selectedPizza.sizes[pizzaSize])\n  }\n

    When the application loads, we don't have our data yet. So we only want to render a selected pizza and price once we are done loading. Add a condition to the JSX to show "Loading..." initially, and then the selected pizza once we are done loading. Add the condition before the closing </form> tag:

    \n
    ...\n  {\n    loading ? (\n      <h3>Loading...</h3>\n    ) : (\n      <div className="order-pizza">\n        <Pizza\n          name={selectedPizza.name}\n          description={selectedPizza.description}\n          image={selectedPizza.image}\n        />\n        <p>{price}</p>\n      </div>\n    )\n  }\n</form>\n
    \n

    🏁 Click here to see the state of the project up until now: 05-effects

    \n
    \n","markdown":"\n## Effects\n\nWe 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.\n\n> Make sure you have both your Vite dev server running _and_ your API server running. Both.\n\nLet's refactor Order.jsx\n\n```javascript\n// change import at top\nimport { useEffect, useState } from \"react\";\nimport Pizza from \"./Pizza\";\n\n// outside of the render function\n// feel free to change en-US / USD to your locale\nconst intl = new Intl.NumberFormat(\"en-US\", {\n style: \"currency\",\n currency: \"USD\",\n});\n\n// add to the other useStates inside component at top\nconst [pizzaTypes, setPizzaTypes] = useState([]);\nconst [loading, setLoading] = useState(true);\n\nlet price, selectedPizza;\nif (!loading) {\n selectedPizza = pizzaTypes.find((pizza) => pizzaType === pizza.id);\n price = intl.format(\n selectedPizza.sizes ? selectedPizza.sizes[pizzaSize] : \"\"\n );\n}\n\nuseEffect(() => {\n fetchPizzaTypes();\n}, []);\n\nasync function fetchPizzaTypes() {\n await new Promise((resolve) => setTimeout(resolve, 3000)); // remove this later, just to show you the loading state\n\n const pizzasRes = await fetch(\"/api/pizzas\");\n const pizzasJson = await pizzasRes.json();\n setPizzaTypes(pizzasJson);\n setLoading(false);\n}\n\n// replace the options\n{\n pizzaTypes.map((pizza) => (\n \n ));\n}\n\n// replace and button at the end\n```\n\n- 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.\n- 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.\n- 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.\n- 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.\n- 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.\n\n### Updating the Selected Pizza & Price\n\nWhen a pizza is selected, we need to update the selected pizza and price. First, let's format the price after the `selectedPizza` state is updated:\n\n```javascript\n if(!loading){\n selectedPizza = pizzaTypes.find((pizza) => pizzaType === pizza.id)\n price = intl.format(selectedPizza.sizes[pizzaSize])\n }\n```\n\nWhen the application loads, we don't have our data yet. So we only want to render a selected pizza and price once we are done loading. Add a condition to the JSX to show \"Loading...\" initially, and then the selected pizza once we are done loading. Add the condition **before** the closing `` tag:\n\n```jsx\n...\n {\n loading ? (\n

    Loading...

    \n ) : (\n
    \n \n

    {price}

    \n
    \n )\n }\n\n```\n\n> 🏁 [Click here to see the state of the project up until now: 05-effects][step]\n\n[step]: https://github.com/btholt/citr-v9-project/tree/master/05-effects\n","slug":"effects","title":"Effects","section":"Core React Concepts","icon":"book","filePath":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/04-core-react-concepts/C-effects.md","nextSlug":"/lessons/core-react-concepts/dev-tools","prevSlug":"/lessons/core-react-concepts/hooks"}},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/core-react-concepts/handling-user-inputs.json b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/core-react-concepts/handling-user-inputs.json new file mode 100644 index 0000000..71d35b3 --- /dev/null +++ b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/core-react-concepts/handling-user-inputs.json @@ -0,0 +1 @@ +{"pageProps":{"post":{"attributes":{"description":"Learn how to handle user cart submissions and checkout process in React with Brian Holt's Complete Intro to React, v9. Build a shopping cart component using Order.jsx and Cart.jsx, manage cart state with hooks, and implement server-side checkout functionality efficiently. Ideal for React developers seeking practical, hands-on experience in web development.","keywords":["React","shopping cart","checkout","Brian Holt","Order.jsx","Cart.jsx","web development"]},"html":"

    Handling User Input

    \n

    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

    \n
    // add import\nimport Cart from "./Cart";\n\n// add another hook\nconst [cart, setCart] = useState([]);\n\n// replace <form>\n<form\n  onSubmit={(e) => {\n    e.preventDefault();\n    setCart([...cart, { pizza: selectedPizza, size: pizzaSize, price }]);\n  }}\n>\n  […]\n</form>;\n\n// just inside the last closing div\n{\n  loading ? <h2>LOADING …</h2> : <Cart cart={cart} />\n}\n

    So now we're using an onSubmit to handle the adding to cart. Awesome! Then we're passing that into Cart to have a nice display.

    \n

    Let's make the cart. Make a file called Cart.jsx and add

    \n
    const intl = new Intl.NumberFormat("en-US", {\n  style: "currency",\n  currency: "USD", // feel free to change to your local currency\n});\n\nexport default function Cart({ cart, checkout }) {\n  let total = 0;\n  for (let i = 0; i < cart.length; i++) {\n    const current = cart[i];\n    total += current.pizza.sizes[current.size];\n  }\n  return (\n    <div className="cart">\n      <h2>Cart</h2>\n      <ul>\n        {cart.map((item, index) => (\n          <li key={index}>\n            <span className="size">{item.size}</span> –\n            <span className="type">{item.pizza.name}</span> –\n            <span className="price">{item.price}</span>\n          </li>\n        ))}\n      </ul>\n      <p>Total: {intl.format(total)}</p>\n      <button onClick={checkout}>Checkout</button>\n    </div>\n  );\n}\n
    \n

    Normally doing key this way is a bad idea, but we wouldn't reorganize this list, nor care if it rerendered anyway, so it gets a pass.

    \n
    \n

    Now we have a nice shopping cart experience. So how do actually checkout on the server? Let's do that! We probably want to do it as the Order level. It already has the Cart and we can just leave the Cart as a dumb display component. We can just pass a function to call into the Cart component and call it and run the function at the Order level.

    \n
    // inside the render body\nasync function checkout() {\n  setLoading(true);\n\n  await fetch("/api/order", {\n    method: "POST",\n    headers: {\n      "Content-Type": "application/json",\n    },\n    body: JSON.stringify({\n      cart,\n    }),\n  });\n\n  setCart([]);\n  setLoading(false);\n}\n\n// replace Cart\n<Cart checkout={checkout} cart={cart} />;\n

    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!

    \n
    \n

    🏁 Click here to see the state of the project up until now: 07-handling-forms

    \n
    \n","markdown":"\n## Handling User Input\n\nSo 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\n\n```javascript\n// add import\nimport Cart from \"./Cart\";\n\n// add another hook\nconst [cart, setCart] = useState([]);\n\n// replace
    \n {\n e.preventDefault();\n setCart([...cart, { pizza: selectedPizza, size: pizzaSize, price }]);\n }}\n>\n […]\n;\n\n// just inside the last closing div\n{\n loading ?

    LOADING …

    : \n}\n```\n\nSo now we're using an onSubmit to handle the adding to cart. Awesome! Then we're passing that into Cart to have a nice display.\n\nLet's make the cart. Make a file called Cart.jsx and add\n\n```javascript\nconst intl = new Intl.NumberFormat(\"en-US\", {\n style: \"currency\",\n currency: \"USD\", // feel free to change to your local currency\n});\n\nexport default function Cart({ cart, checkout }) {\n let total = 0;\n for (let i = 0; i < cart.length; i++) {\n const current = cart[i];\n total += current.pizza.sizes[current.size];\n }\n return (\n
    \n

    Cart

    \n
      \n {cart.map((item, index) => (\n
    • \n {item.size} –\n {item.pizza.name} –\n {item.price}\n
    • \n ))}\n
    \n

    Total: {intl.format(total)}

    \n \n
    \n );\n}\n```\n\n> Normally doing key this way is a bad idea, but we wouldn't reorganize this list, nor care if it rerendered anyway, so it gets a pass.\n\nNow we have a nice shopping cart experience. So how do actually checkout on the server? Let's do that! We probably want to do it as the Order level. It already has the Cart and we can just leave the Cart as a dumb display component. We can just pass a function to call into the Cart component and call it and run the function at the Order level.\n\n```javascript\n// inside the render body\nasync function checkout() {\n setLoading(true);\n\n await fetch(\"/api/order\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n cart,\n }),\n });\n\n setCart([]);\n setLoading(false);\n}\n\n// replace Cart\n;\n```\n\nNow 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!\n\n> 🏁 [Click here to see the state of the project up until now: 07-handling-forms][step]\n\n[step]: https://github.com/btholt/citr-v9-project/tree/master/07-handling-forms\n","slug":"handling-user-inputs","title":"Handling User Inputs","section":"Core React Concepts","icon":"book","filePath":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/04-core-react-concepts/F-handling-user-inputs.md","nextSlug":"/lessons/core-react-concepts/context","prevSlug":"/lessons/core-react-concepts/custom-hooks"}},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/core-react-concepts/hooks.json b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/core-react-concepts/hooks.json new file mode 100644 index 0000000..4cf3ffa --- /dev/null +++ b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/core-react-concepts/hooks.json @@ -0,0 +1 @@ +{"pageProps":{"post":{"attributes":{"description":"Learn how to create an interactive form in React with useState hooks, allowing users to add pizzas to their order in this detailed coding guide from the \"Complete Intro to React, v9\" course by Brian Holt. Explore concepts like controlled inputs, JSX syntax, event handling, and the importance of state management in React development. Enhance your web development skills with practical React examples and create dynamic applications effectively.","keywords":["React tutorial","useState hooks","controlled inputs","web development","Brian Holt"]},"html":"

    Hooks

    \n

    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:

    \n
    import Pizza from "./Pizza";\n\nexport default function Order() {\n  const pizzaType = "pepperoni";\n  const pizzaSize = "M";\n  return (\n    <div className="order">\n      <h2>Create Order</h2>\n      <form>\n        <div>\n          <div>\n            <label htmlFor="pizza-type">Pizza Type</label>\n            <select name="pizza-type" value={pizzaType}>\n              <option value="pepperoni">The Pepperoni Pizza</option>\n              <option value="hawaiian">The Hawaiian Pizza</option>\n              <option value="big_meat">The Big Meat Pizza</option>\n            </select>\n          </div>\n          <div>\n            <label htmlFor="pizza-size">Pizza Size</label>\n            <div>\n              <span>\n                <input\n                  checked={pizzaSize === "S"}\n                  type="radio"\n                  name="pizza-size"\n                  value="S"\n                  id="pizza-s"\n                />\n                <label htmlFor="pizza-s">Small</label>\n              </span>\n              <span>\n                <input\n                  checked={pizzaSize === "M"}\n                  type="radio"\n                  name="pizza-size"\n                  value="M"\n                  id="pizza-m"\n                />\n                <label htmlFor="pizza-m">Medium</label>\n              </span>\n              <span>\n                <input\n                  checked={pizzaSize === "L"}\n                  type="radio"\n                  name="pizza-size"\n                  value="L"\n                  id="pizza-l"\n                />\n                <label htmlFor="pizza-l">Large</label>\n              </span>\n            </div>\n          </div>\n          <button type="submit">Add to Cart</button>\n        </div>\n        <div className="order-pizza">\n          <Pizza\n            name="Pepperoni"\n            description="Mozzarella Cheese, Pepperoni"\n            image="/public/pizzas/pepperoni.webp"\n          />\n          <p>$13.37</p>\n        </div>\n      </form>\n    </div>\n  );\n}\n

    Now add it to your App.jsx:

    \n
    // delete Pizza import, and add Order\nimport Order from "./Order";\n\n// in App.jsx, replace all the Pizzas\n<Order />\n
    \n

    🚨 You'll have some errors in the console, that's okay.

    \n
    \n

    Now navigate to http://localhost:5173/ and see that you have two inputs, one for the pizza type and a set of radio buttons for the size. Try and select something with the inputs. You'll see that you can't modify them. Why? Let's think about how React works: when you interact with the inputs, React detects that a DOM event happens. When that happens, React thinks something may have changed so it runs a re-render. Providing your render functions are fast, this is a very quick operation. It then diffs what's currently there and what its render pass came up with. It then updates the minimum amount of DOM necessary.

    \n
    \n

    In the recorded course, the layout is vertical because the order-pizza DIV was in the wrong place. Use the markup above. The layout should look like this:

    \n
    \n

    \"Order

    \n

    Notice we're using className instead of class on the HTML element for CSS classes. This is because class is a reserved word in JS and JSX is still just JS. So instead they opted to use className which is the name of the JS API for interacting with class names.

    \n

    Like className, htmlFor is used because for is a reserved word in JS.

    \n

    So if we type in our input and it re-renders, what gets out in the select or radio button tags? Well, its value is tied to pizzaType and pizzaSize and nothing changed those, so they remain the same. In other words, two way data binding is not free in React. I say this is a feature because it makes you explicit on how you handle your data. Let's go make it work.

    \n
    // in Order.jsx\nimport { useState } from "react";\n\n// pizzaType and pizzaSize location\nconst [pizzaType, setPizzaType] = useState("pepperoni");\nconst [pizzaSize, setPizzaSize] = useState("medium");\n\n// replace input\n<select\n  onChange={(e) => setPizzaType(e.target.value)}\n  name="pizza-type"\n  value={pizzaType}\n>\n  […]\n</select>\n\n// add to all the radio buttons\nonChange={(e) => setPizzaSize(e.target.value)}\n
      \n
    • This is called a hook. Other frameworks like Vue have adopted it as well.
    • \n
    • A hook called such (in my head) because it's a hook that gets caught every time the render function gets called. Because the hooks get called in the same order every single time, they'll always point to the same piece of state. Because of that they can be stateful: you can keep pieces of mutable state using hooks and then modify them later using their provided updater functions.
    • \n
    • An absolutely key concept for you to grasp is hooks rely on this strict ordering. As such, do not put hooks inside if statements or loops. If you do, you'll have insane bugs that involve useState returning the wrong state. If you see useState returning the wrong piece of state, this is likely what you did. Every hook must run every time in the same order.
    • \n
    • The argument given to useState is the default value. In our case, we could give it "" as our default value to make the user have to select something first but in our case we want to default to pepperoni pizza and medium size.
    • \n
    • 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.
    • \n
    • 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.
    • \n
    • You can make your own custom hooks; useState is just one of many.
    • \n
    • 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.
    • \n
    • 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.
    • \n
    • 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.
    • \n
    • Similar to above. We're using onChange because it makes it more accessible.
    • \n
    \n
    \n

    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.\nAlso what's new in React is called a "form action" that is considered unstable. In the future you will just add <form action="blah">[…]</form> and a form action will handle the entire form for you.

    \n
    \n

    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.

    \n
    // you could replace the div surrounding the radio buttons and remove all the onChange handlers\n<div onChange={(e) => setPizzaSize(e.target.value)}>[…]</div>\n
    \n

    🏁 Click here to see the state of the project up until now: 04-hooks

    \n
    \n","markdown":"\n## Hooks\n\nOkay, 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:\n\n```javascript\nimport Pizza from \"./Pizza\";\n\nexport default function Order() {\n const pizzaType = \"pepperoni\";\n const pizzaSize = \"M\";\n return (\n
    \n

    Create Order

    \n
    \n
    \n
    \n \n \n
    \n
    \n \n
    \n \n \n \n \n \n \n \n \n \n \n \n \n
    \n
    \n \n
    \n
    \n \n

    $13.37

    \n
    \n
    \n
    \n );\n}\n```\n\nNow add it to your App.jsx:\n\n```javascript\n// delete Pizza import, and add Order\nimport Order from \"./Order\";\n\n// in App.jsx, replace all the Pizzas\n\n```\n\n> 🚨 You'll have some errors in the console, that's okay.\n\nNow navigate to [http://localhost:5173/]() and see that you have two inputs, one for the pizza type and a set of radio buttons for the size. Try and select something with the inputs. You'll see that you can't modify them. Why? Let's think about how React works: when you interact with the inputs, React detects that a DOM event happens. When that happens, React thinks _something_ may have changed so it runs a re-render. Providing your render functions are fast, this is a very quick operation. It then diffs what's currently there and what its render pass came up with. It then updates the minimum amount of DOM necessary.\n\n> In the recorded course, the layout is vertical because the `order-pizza` DIV was in the wrong place. Use the markup above. The layout should look like this:\n\n\n![Order Page Layout](/images/order-page-layout.webp)\n\n\nNotice we're using `className` instead of `class` on the HTML element for CSS classes. This is because `class` is a reserved word in JS and JSX is still just JS. So instead they opted to use `className` which is the [name of the JS API][js-api] for interacting with class names.\n\nLike `className`, `htmlFor` is used because `for` is a reserved word in JS.\n\nSo if we type in our input and it re-renders, what gets out in the `select` or radio button tags? Well, its value is tied to `pizzaType` and `pizzaSize` and nothing changed those, so they remain the same. In other words, two way data binding is _not_ free in React. I say this is a feature because it makes you explicit on how you handle your data. Let's go make it work.\n\n```javascript\n// in Order.jsx\nimport { useState } from \"react\";\n\n// pizzaType and pizzaSize location\nconst [pizzaType, setPizzaType] = useState(\"pepperoni\");\nconst [pizzaSize, setPizzaSize] = useState(\"medium\");\n\n// replace input\n setPizzaType(e.target.value)}\n name=\"pizza-type\"\n value={pizzaType}\n>\n […]\n\n\n// add to all the radio buttons\nonChange={(e) => setPizzaSize(e.target.value)}\n```\n\n- This is called a hook. Other frameworks like Vue have adopted it as well.\n- A hook called such (in my head) because it's a hook that gets caught every time the render function gets called. Because the hooks get called in the same order every single time, they'll always point to the same piece of state. Because of that they can be stateful: you can keep pieces of mutable state using hooks and then modify them later using their provided updater functions.\n- An _absolutely key_ concept for you to grasp is hooks rely on this strict ordering. As such, **do not put hooks inside if statements or loops**. If you do, you'll have insane bugs that involve `useState` returning _the wrong state_. If you see `useState` returning the wrong piece of state, this is likely what you did. Every hook must run every time in the same order.\n- The argument given to `useState` is the default value. In our case, we could give it `\"\"` as our default value to make the user have to select something first but in our case we want to default to pepperoni pizza and medium size.\n- `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.\n- 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.\n- You can make your own custom hooks; `useState` is just one of many.\n- 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.\n- 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.\n- 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.\n- Similar to above. We're using `onChange` because it makes it more accessible.\n\n> 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.\n> 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.\n\nAnother 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.\n\n```javascript\n// you could replace the div surrounding the radio buttons and remove all the onChange handlers\n
    setPizzaSize(e.target.value)}>[…]
    \n```\n\n> 🏁 [Click here to see the state of the project up until now: 04-hooks][step]\n\n[babel]: https://babeljs.io/\n[step]: https://github.com/btholt/citr-v9-project/tree/main/04-hooks\n[js-api]: https://developer.mozilla.org/en-US/docs/Web/API/Element/className\n","slug":"hooks","title":"Hooks","section":"Core React Concepts","icon":"book","filePath":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/04-core-react-concepts/B-hooks.md","nextSlug":"/lessons/core-react-concepts/effects","prevSlug":"/lessons/core-react-concepts/jsx"}},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/core-react-concepts/jsx.json b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/core-react-concepts/jsx.json new file mode 100644 index 0000000..fc6532b --- /dev/null +++ b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/core-react-concepts/jsx.json @@ -0,0 +1 @@ +{"pageProps":{"post":{"attributes":{"title":"JSX","description":"Explore the integration of JSX in React applications with Brian Holt's \"Complete Intro to React, v9\" course, as it simplifies HTML tags into React.createElement calls, enhancing code readability. Learn about configuring ESLint for JSX, React components, and Vite server setup for a complete React development experience.","keywords":["React","JSX","Brian Holt","frontend","JavaScript","web development","Vite"]},"html":"

    JSX

    \n

    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.

    \n

    If I write React.createElement("h1", { id: "main-title" }, "My Website");, what am I actually trying to have rendered out? <h1 id="main-title">My Website</h1>, right? What JSX tries to do is to shortcut this translation layer in your brain so you can just write what you mean.

    \n

    Make a new file called Pizza.jsx.

    \n
    \n

    Make sure you call it .jsx and not .js. Vite won't do JSX transpilation if it's not named with a JSX file extension.

    \n
    \n
    const Pizza = (props) => {\n  return (\n    <div className="pizza">\n      <h1>{props.name}</h1>\n      <p>{props.description}</p>\n    </div>\n  );\n};\n\nexport default Pizza;\n

    I don't know about you, but I find this far more readable. And if it feels uncomfortable to you to introduce HTML into your JavaScript, I invite you to give it a shot until the end of the workshop. By then it should feel a bit more comfortable. And you can always go back to the old way.

    \n

    However, now you know what JSX is doing for you. It's just translating those HTML tags into React.createElement calls. That's it. Really. No more magic here. JSX does nothing else. Many people who learn React don't learn this.

    \n

    Notice the strange {props.name} syntax: this is how you output JavaScript expressions in JSX. An expression is anything that can be the right side of an assignment operator in JavaScript, e.g. const x = <anything that can go here>. If you take away the {} it will literally output props.name to the DOM.

    \n
    \n

    Notice we don't have to do import React from 'react' here like we used to. The latest version of JSX handles that for you so you only need to explicitly import the React package when you need to use something from it; otherwise feel free to do JSX without having to import React!

    \n
    \n

    ESLint

    \n

    Let's fix our ESLint as JSX adds new twists and turns we need help with. Please install

    \n
    npm i -D eslint-plugin-react@7.37.1\n

    Then in your eslint.config.mjs

    \n
    // at top, import the ESLint reactPlugin\nimport reactPlugin from "eslint-plugin-react";\n\n// under js.configs.recommended line\n{\n  ...reactPlugin.configs.flat.recommended,\n  settings: {\n    react: {\n      version: "detect",\n    },\n  },\n},\nreactPlugin.configs.flat["jsx-runtime"],\n\n// add to files\nfiles: ["**/*.js", "**/*.jsx"], // add JSX\n\n//after the languageOptions object:\nrules: {\n  "react/no-unescaped-entities": "off",\n  "react/prop-types": "off",\n},\n

    You can also copy fill config from the repo.

    \n

    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.

    \n

    We're also turning off two rules that I don't find particularly useful: unescaped entities (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.

    \n

    Back to JSX

    \n

    So now JSX is demystified a bit, let's go convert App.js.

    \n
    // rename the file App.jsx\n// delete the React import\nimport { createRoot } from "react-dom/client";\nimport Pizza from "./Pizza";\n\n// delete the Pizza component\n\nconst App = () => {\n  return (\n    <div>\n      <h1>Padre Gino's Pizza – Order Now</h1>\n      <Pizza name="Pepperoni" description="Mozzarella Cheese, Pepperoni" />\n      <Pizza\n        name="The Hawaiian Pizza"\n        description="Sliced Ham, Pineapple, Mozzarella Cheese"\n      />\n      <Pizza\n        name="The Big Meat Pizza"\n        description="Bacon, Pepperoni, Italian Sausage, Chorizo Sausage"\n      />\n    </div>\n  );\n};\n\nconst container = document.getElementById("root");\nconst root = createRoot(container);\nroot.render(<App />);\n

    Also head over to index.html and change the script tag

    \n
    <script type="module" src="./src/App.jsx"></script>\n

    Notice we have Pizza as a component. Notice that the P in Pizza is capitalized. It must be. If you make it lowercase, it will try to have pizza as a web component and not a React component.

    \n

    We now pass props down as we add attributes to an HTML tag. Pretty cool.

    \n

    You can test your app by running npm run dev and opening the URL shown in the terminal. It's typically http://localhost:5173/

    \n

    The API / Image Server

    \n

    For this course we will use a little Fastify server I built for you. It's in the api directory. We are going to use Vite.js to proxy to this API server. This is a useful trick to do for local development if you have a separate frontend in a backend. Normally you'd have something like NGINX routing traffic to separate frontend and backend servers. For now we'll just use Vite.

    \n
    \n

    Note: This means you'll need to have TWO terminal windows running. One terminal for the API server (which you won't have to touch once it's running). The other terminal is is our Vite server for our web app.

    \n
    \n

    Add this to you vite.config.js

    \n
    // replace export\nexport default defineConfig({\n  server: {\n    proxy: {\n      "/api": {\n        target: "http://localhost:3000",\n        changeOrigin: true,\n      },\n      "/public": {\n        target: "http://localhost:3000",\n        changeOrigin: true,\n      },\n    },\n  },\n  plugins: [react()],\n});\n

    Run the API Server

    \n

    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 npm run dev. 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!

    \n

    Now let's add images to our Pizza.

    \n
    // return inside Pizza, inside div, under <p>\n<img src={props.image} alt={props.name} />\n

    Now in App.jsx

    \n
    // add to first Pizza\nimage={"/public/pizzas/pepperoni.webp"}\n\n// add to second Pizza\nimage={"/public/pizzas/hawaiian.webp"}\n\n// add to third Pizza\nimage={"/public/pizzas/big_meat.webp"}\n

    And now you should have images!

    \n
    \n

    🏁 Click here to see the state of the project up until now: 03-jsx

    \n
    \n","markdown":"\n## JSX\n\nSo 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.\n\nIf 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.\n\nMake a new file called Pizza.jsx.\n\n> Make sure you call it `.jsx` and not `.js`. Vite won't do JSX transpilation if it's not named with a JSX file extension.\n\n```javascript\nconst Pizza = (props) => {\n return (\n
    \n

    {props.name}

    \n

    {props.description}

    \n
    \n );\n};\n\nexport default Pizza;\n```\n\nI don't know about you, but I find this far more readable. And if it feels uncomfortable to you to introduce HTML into your JavaScript, I invite you to give it a shot until the end of the workshop. By then it should feel a bit more comfortable. And you can always go back to the old way.\n\nHowever, now you know _what_ JSX is doing for you. It's just translating those HTML tags into `React.createElement` calls. _That's it._ Really. No more magic here. JSX does nothing else. Many people who learn React don't learn this.\n\nNotice the strange `{props.name}` syntax: this is how you output JavaScript expressions in JSX. An expression is anything that can be the right side of an assignment operator in JavaScript, e.g. `const x = `. If you take away the `{}` it will literally output `props.name` to the DOM.\n\n> Notice we don't have to do `import React from 'react'` here like we used to. The latest version of JSX handles that for you so you only need to explicitly import the React package when you need to use something from it; otherwise feel free to do JSX without having to import React!\n\n## ESLint\n\nLet's fix our ESLint as JSX adds new twists and turns we need help with. Please install\n\n```bash\nnpm i -D eslint-plugin-react@7.37.1\n```\n\nThen in your eslint.config.mjs\n\n```javascript\n// at top, import the ESLint reactPlugin\nimport reactPlugin from \"eslint-plugin-react\";\n\n// under js.configs.recommended line\n{\n ...reactPlugin.configs.flat.recommended,\n settings: {\n react: {\n version: \"detect\",\n },\n },\n},\nreactPlugin.configs.flat[\"jsx-runtime\"],\n\n// add to files\nfiles: [\"**/*.js\", \"**/*.jsx\"], // add JSX\n\n//after the languageOptions object:\nrules: {\n \"react/no-unescaped-entities\": \"off\",\n \"react/prop-types\": \"off\",\n},\n```\n\nYou can also copy fill config from [the repo][eslint].\n\nWe 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.\n\nWe're also turning off two rules that I don't find particularly useful: unescaped entities (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.\n\n## Back to JSX\n\nSo now JSX is demystified a bit, let's go convert App.js.\n\n```javascript\n// rename the file App.jsx\n// delete the React import\nimport { createRoot } from \"react-dom/client\";\nimport Pizza from \"./Pizza\";\n\n// delete the Pizza component\n\nconst App = () => {\n return (\n
    \n

    Padre Gino's Pizza – Order Now

    \n \n \n \n
    \n );\n};\n\nconst container = document.getElementById(\"root\");\nconst root = createRoot(container);\nroot.render();\n```\n\nAlso head over to index.html and change the script tag\n\n```html\n\n```\n\nNotice we have Pizza as a component. Notice that the `P` in `Pizza` is capitalized. It _must_ be. If you make it lowercase, it will try to have `pizza` as a web component and not a React component.\n\nWe now pass props down as we add attributes to an HTML tag. Pretty cool.\n\nYou can test your app by running `npm run dev` and opening the URL shown in the terminal. It's typically [http://localhost:5173/]()\n\n## The API / Image Server\n\nFor this course we will use a little Fastify server I built for you. It's in the [api][api] directory. We are going to use Vite.js to proxy to this API server. This is a useful trick to do for local development if you have a separate frontend in a backend. Normally you'd have something like NGINX routing traffic to separate frontend and backend servers. For now we'll just use Vite.\n\n> Note: This means you'll need to have TWO terminal windows running. One terminal for the API server (which you won't have to touch once it's running). The other terminal is is our Vite server for our web app. \n\nAdd this to you vite.config.js\n\n```javascript\n// replace export\nexport default defineConfig({\n server: {\n proxy: {\n \"/api\": {\n target: \"http://localhost:3000\",\n changeOrigin: true,\n },\n \"/public\": {\n target: \"http://localhost:3000\",\n changeOrigin: true,\n },\n },\n },\n plugins: [react()],\n});\n```\n\n## Run the API Server\n\nOpen 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 `npm run dev`. 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!\n\nNow let's add images to our Pizza.\n\n```javascript\n// return inside Pizza, inside div, under

    \n{props.name}\n```\n\nNow in App.jsx\n\n```javascript\n// add to first Pizza\nimage={\"/public/pizzas/pepperoni.webp\"}\n\n// add to second Pizza\nimage={\"/public/pizzas/hawaiian.webp\"}\n\n// add to third Pizza\nimage={\"/public/pizzas/big_meat.webp\"}\n```\n\nAnd now you should have images!\n\n> 🏁 [Click here to see the state of the project up until now: 03-jsx][step]\n\n[airbnb]: https://github.com/airbnb/javascript/tree/master/packages/eslint-config-airbnb\n[standard]: https://standardjs.com/\n[step]: https://github.com/btholt/citr-v9-project/tree/master/03-jsx\n[api]: https://github.com/btholt/citr-v9-project/tree/main/api\n[eslint]: https://github.com/btholt/citr-v9-project/blob/main/03-jsx/eslint.config.mjs\n","slug":"jsx","title":"JSX","section":"Core React Concepts","icon":"book","filePath":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/04-core-react-concepts/A-jsx.md","nextSlug":"/lessons/core-react-concepts/hooks","prevSlug":"/lessons/tools/vite"}},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/ecosystem/tanstack-query.json b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/ecosystem/tanstack-query.json new file mode 100644 index 0000000..3887eca --- /dev/null +++ b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/ecosystem/tanstack-query.json @@ -0,0 +1 @@ +{"pageProps":{"post":{"attributes":{"title":"TanStack Query","description":"Learn how to create a past orders page in React using TanStack Query to facilitate seamless asynchronous API calls, enhancing code cleanliness and efficiency. This guide by seasoned developer Brian Holt provides detailed instructions on integrating React Query ESLint configurations and leveraging React Query Devtools for better debugging. Ideal for web developers looking to optimize React-based projects with effective data fetching techniques.","keywords":["React","TanStack Query","API calls","web development","JavaScript","Brian Holt"]},"html":"

    Tanstack Query

    \n

    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.

    \n

    Now let's install our next tool, TanStack (React) Query.

    \n
    npm i @tanstack/react-query@5.59.13\nnpm i -D @tanstack/react-query-devtools@5.59.13 @tanstack/eslint-plugin-query@5.59.7\n

    React Query makes doing async API calls so much easier. So much easier / better, that I almost never use useEffect anymore like we did for the pizzas list or for the pizza of the day. The code is cleaner, easier to read, better cached, and less bug prone. In short, it's just better. It's good for you to know how to use effects, but as you go forward just use TanStack Query for API calls.

    \n

    Let's get started. Let's start by adding their ESLint config to ours. They have some useful rules in there. In eslint.config.mjs

    \n
    // at top\nimport pluginQuery from "@tanstack/eslint-plugin-query";\n\n// under reactPlugin.configs.flat["jsx-runtime"]\n...pluginQuery.configs["flat/recommended"],\n

    This will add a few rules to your ESLint that are specific to React Query. I have found these helpful, and more importantly, not annoying, so I tend to add them every time.

    \n

    Let's also add the dev tools, like we did for the router. In src/routes/__root.jsx:

    \n
    // at top\nimport { ReactQueryDevtools } from "@tanstack/react-query-devtools";\n\n// under router dev tools\n<ReactQueryDevtools />\n

    Finally, we need to add the QueryClient. In App.jsx, add:

    \n
    // Add imports\nimport { QueryClient, QueryClientProvider } from "@tanstack/react-query";\n\n// Create a property under the router\nconst queryClient = new QueryClient()\n\n// Add the provider to the app\n<QueryClientProvider client={queryClient}>\n  <RouterProvider router={router} />\n</QueryClientProvider>\n

    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.

    \n

    So react-query makes interacting with APIs very simple and makes it easy to read. You just read a hook and it'll either give you a isLoading status or the data. Once the data comes back, it'll refresh the component with the data. So let's start by writing our very simple fetch call. Create a folder called api inside of src and create getPastOrders.js and put

    \n
    export default async function getPastOrders(page) {\n  const response = await fetch(`/api/past-orders?page=${page}`);\n  const data = await response.json();\n  return data;\n}\n

    Very simple request to an API that returns data. That's it!

    \n

    Let's now go make past.lazy.jsx.

    \n
    import { useState } from "react";\nimport { useQuery } from "@tanstack/react-query";\nimport { createLazyFileRoute } from "@tanstack/react-router";\nimport getPastOrders from "../api/getPastOrders";\n\nexport const Route = createLazyFileRoute("/past")({\n  component: PastOrdersRoute,\n});\n\nfunction PastOrdersRoute() {\n  const [page, setPage] = useState(1);\n  const { isLoading, data } = useQuery({\n    queryKey: ["past-orders", page],\n    queryFn: () => getPastOrders(page),\n    staleTime: 30000,\n  });\n  if (isLoading) {\n    return (\n      <div className="past-orders">\n        <h2>LOADING …</h2>\n      </div>\n    );\n  }\n  return (\n    <div className="past-orders">\n      <table>\n        <thead>\n          <tr>\n            <td>ID</td>\n            <td>Date</td>\n            <td>Time</td>\n          </tr>\n        </thead>\n        <tbody>\n          {data.map((order) => (\n            <tr key={order.order_id}>\n              <td>{order.order_id}</td>\n              <td>{order.date}</td>\n              <td>{order.time}</td>\n            </tr>\n          ))}\n        </tbody>\n      </table>\n      <div className="pages">\n        <button disabled={page <= 1} onClick={() => setPage(page - 1)}>\n          Previous\n        </button>\n        <div>{page}</div>\n        <button disabled={data.length < 10} onClick={() => setPage(page + 1)}>\n          Next\n        </button>\n      </div>\n    </div>\n  );\n}\n
      \n
    • We're using hooks to track the page we're on.
    • \n
    • We're using the useQuery hook to make API calls and we provide it the queryFn of how to go fetch that data.
    • \n
    • We're giving it keys which act as cache keys. We're giving past-orders as the key but it could be any unique key to this page. Then we give it the page. What's cool about this is that while we will request the page 1 the first time we request it, the second time we request page 1 it'll see it'll see that we already have this in cache and not request it. How cool is is that? That used to require so much logic to handle it and now React Query just does it for you.
    • \n
    • Now open the dev tools. You can see all the pages being loaded in. Pretty cool, right?
    • \n
    • Try taking out page from the query key. It'll yell at you. This is the ESLint config we pulled in from Tanstack Query. Because we're using that page in the request, we need to use it as a caching key. If you depend on a variable to make a request, it should be apart of the caching key.
    • \n
    • We're giving it a staleTime of 30 seconds (30,000 milliseconds). This allows someone using the page to browse around a bit and not bombard the API too much but the page won't ever be too stale. If you omit staleTime, it will refetch every time.
    • \n
    \n

    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.

    \n
    \n

    🏁 Click here to see the state of the project up until now: 10-query

    \n
    \n","markdown":"\n## Tanstack Query\n\nLet'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.\n\nNow let's install our next tool, [TanStack (React) Query][tsq].\n\n```bash\nnpm i @tanstack/react-query@5.59.13\nnpm i -D @tanstack/react-query-devtools@5.59.13 @tanstack/eslint-plugin-query@5.59.7\n```\n\nReact Query makes doing async API calls so much easier. So much easier / better, that I almost never use `useEffect` anymore like we did for the pizzas list or for the pizza of the day. The code is cleaner, easier to read, better cached, and less bug prone. In short, it's just better. It's good for you to know how to use effects, but as you go forward just use TanStack Query for API calls.\n\nLet's get started. Let's start by adding their ESLint config to ours. They have some useful rules in there. In eslint.config.mjs\n\n```javascript\n// at top\nimport pluginQuery from \"@tanstack/eslint-plugin-query\";\n\n// under reactPlugin.configs.flat[\"jsx-runtime\"]\n...pluginQuery.configs[\"flat/recommended\"],\n```\n\nThis will add a few rules to your ESLint that are specific to React Query. I have found these helpful, and more importantly, not annoying, so I tend to add them every time.\n\nLet's also add the dev tools, like we did for the router. In `src/routes/__root.jsx`:\n\n```javascript\n// at top\nimport { ReactQueryDevtools } from \"@tanstack/react-query-devtools\";\n\n// under router dev tools\n\n```\n\nFinally, we need to add the `QueryClient`. In `App.jsx`, add:\n\n```javascript\n// Add imports\nimport { QueryClient, QueryClientProvider } from \"@tanstack/react-query\";\n\n// Create a property under the router\nconst queryClient = new QueryClient()\n\n// Add the provider to the app\n\n \n\n```\n\nNow 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.\n\nSo react-query makes interacting with APIs very simple and makes it easy to read. You just read a hook and it'll either give you a `isLoading` status or the data. Once the data comes back, it'll refresh the component with the data. So let's start by writing our very simple fetch call. Create a folder called `api` inside of `src` and create getPastOrders.js and put\n\n```javascript\nexport default async function getPastOrders(page) {\n const response = await fetch(`/api/past-orders?page=${page}`);\n const data = await response.json();\n return data;\n}\n```\n\nVery simple request to an API that returns data. That's it!\n\nLet's now go make past.lazy.jsx.\n\n```javascript\nimport { useState } from \"react\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport { createLazyFileRoute } from \"@tanstack/react-router\";\nimport getPastOrders from \"../api/getPastOrders\";\n\nexport const Route = createLazyFileRoute(\"/past\")({\n component: PastOrdersRoute,\n});\n\nfunction PastOrdersRoute() {\n const [page, setPage] = useState(1);\n const { isLoading, data } = useQuery({\n queryKey: [\"past-orders\", page],\n queryFn: () => getPastOrders(page),\n staleTime: 30000,\n });\n if (isLoading) {\n return (\n
    \n

    LOADING …

    \n
    \n );\n }\n return (\n
    \n \n \n \n \n \n \n \n \n \n {data.map((order) => (\n \n \n \n \n \n ))}\n \n
    IDDateTime
    {order.order_id}{order.date}{order.time}
    \n
    \n \n
    {page}
    \n \n
    \n
    \n );\n}\n```\n\n- We're using hooks to track the page we're on.\n- We're using the useQuery hook to make API calls and we provide it the queryFn of how to go fetch that data.\n- We're giving it keys which act as cache keys. We're giving `past-orders` as the key but it could be any unique key to this page. Then we give it the page. What's cool about this is that while we will request the page 1 the first time we request it, the second time we request page 1 it'll see it'll see that we already have this in cache and not request it. How cool is is that? That used to require so much logic to handle it and now React Query just does it for you.\n- Now open the dev tools. You can see all the pages being loaded in. Pretty cool, right?\n- Try taking out `page` from the query key. It'll yell at you. This is the ESLint config we pulled in from Tanstack Query. Because we're using that `page` in the request, we need to use it as a caching key. If you depend on a variable to make a request, it should be apart of the caching key.\n- We're giving it a `staleTime` of 30 seconds (30,000 milliseconds). This allows someone using the page to browse around a bit and not bombard the API too much but the page won't ever be too stale. If you omit `staleTime`, it will refetch every time.\n\nThat'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.\n\n> 🏁 [Click here to see the state of the project up until now: 10-query][step]\n\n[step]: https://github.com/btholt/citr-v9-project/tree/master/10-query\n[tsq]: https://tanstack.com/query/latest\n","slug":"tanstack-query","title":"TanStack Query","section":"Ecosystem","icon":"seedling","filePath":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/05-ecosystem/B-tanstack-query.md","nextSlug":"/lessons/advanced-react/portals","prevSlug":"/lessons/ecosystem/tanstack-router"}},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/ecosystem/tanstack-router.json b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/ecosystem/tanstack-router.json new file mode 100644 index 0000000..fa9fdac --- /dev/null +++ b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/ecosystem/tanstack-router.json @@ -0,0 +1 @@ +{"pageProps":{"post":{"attributes":{"title":"TanStack Router","description":"Discover how to implement routing in React using TanStack Router with insights from Brian Holt's Complete Intro to React v9. Learn about client-side focused routing solutions, file-based route generation, and lazy loading techniques to optimize your React applications.","keywords":["React","TanStack Router","routing","client-side","Brian Holt"]},"html":"

    Tanstack Router

    \n

    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.

    \n

    Historically I have taught react-router 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 use React Router if you want to learn more about it from me.

    \n

    TanStack Router

    \n

    Today we are going to be using TanStack Router, another amazing router. Why the switch? Two reasons:

    \n
      \n
    • 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).
    • \n
    • In Intermediate React we will be covering more full-stack use cases, in which I will using Next.js to show off those features.
    • \n
    \n

    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.

    \n

    Let's start by installing it.

    \n
    npm install @tanstack/react-router@1.65.0\nnpm install -D @tanstack/router-plugin@1.65.0 @tanstack/router-devtools@1.65.0\n

    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.

    \n
    // at top\nimport { TanStackRouterVite } from "@tanstack/router-plugin/vite";\n\n// add before react() in plugins\nplugins: [TanStackRouterVite(), react()],\n

    TanStack Router works by file-based conventions. You create routes in a specific way and TanStack Router will automatically glue it together for you. It does this by creating a file called routeTree.gen.ts. Even though this isn't a TypeScript project, the fact that this is TypeScript means that VS Code can read the types from your routes and help you with suggestions and intelligent errors. I would suggest adding this file to your .gitignore as well since it will get autogenerated with every build. Okay, so let's go make the files we need.

    \n

    Create a routes directory in your src directory. Let's make a __root.jsx file in there. This file will be the base template used for every route. Most of this will come from App.jsx

    \n
    import { useState } from "react";\nimport { createRootRoute, Outlet } from "@tanstack/react-router";\nimport { TanStackRouterDevtools } from "@tanstack/router-devtools";\nimport PizzaOfTheDay from "../PizzaOfTheDay";\nimport Header from "../Header";\nimport { CartContext } from "../contexts";\n\nexport const Route = createRootRoute({\n  component: () => {\n    const cartHook = useState([]);\n    return (\n      <>\n        <CartContext.Provider value={cartHook}>\n          <div>\n            <Header />\n            <Outlet />\n            <PizzaOfTheDay />\n          </div>\n        </CartContext.Provider>\n        <TanStackRouterDevtools />\n      </>\n    );\n  },\n});\n
      \n
    • We added an <Outlet/> instead of our Order component. Now we can swap in new routes there! Notice that the Header will always be there as will the PizzaOfTheDay.
    • \n
    • We added TanStack's excellent dev tools. We'll take a look at them in a bit, but they're in the bottom left, you can click on them once your app loads.
    • \n
    • <> and </> are for when you want to render two sibling components (our context and our dev tools) but don't want to add a random div there. React requires you only return one top level component and we can do that with <> and </>
    • \n
    \n

    Great, let's go modify App.jsx now

    \n
    // add at top\n// remove useState import from react import\nimport { RouterProvider, createRouter } from "@tanstack/react-router";\nimport { routeTree } from "./routeTree.gen";\n\nconst router = createRouter({ routeTree });\n\n// replace App\nconst App = () => {\n  return (\n    <StrictMode>\n      <RouterProvider router={router} />\n    </StrictMode>\n  );\n};\n

    This just imports the generated routeTree and then makes use of it in the project. This file really only should be used for rendering the file. Everything else should likely live in __root.jsx.

    \n

    Okay, move Order.jsx from the base directory and into routes. If it asks, say yes to update paths. Rename it to order.lazy.jsx Let's modify it now to make it a route.

    \n
    // at top\nimport { createLazyFileRoute } from "@tanstack/react-router";\n\n// make sure you modified the relative paths here – VS Code may have done this for you already\nimport { CartContext } from "../contexts";\nimport Cart from "../Cart";\nimport Pizza from "../Pizza";\n\nexport const Route = createLazyFileRoute("/order")({\n  component: Order,\n});\n\nfunction Order() {\n  // Order component code …\n}\n
      \n
    • Here we're making a new route. We define what URL it's at, /order, and what component to render, Order.
    • \n
    • We're making it lazy. It will now code split this for us and lazy-load our route for us. This really helps in large apps. In this tiny app it probably actually slows us down because adding 1KB to an app doesn't slow it down, but an extra round trip does. When you do lazy loading, please measure if it's helping. In any case, this is how you do it.
    • \n
    \n

    That's it! This used to be so hard to code split. In some of my old classes we had to go through some serious work to get it work but now this is all it takes! Pretty cool.

    \n

    Let's add a home page! Make a file called index.lazy.jsx in the routes folder.

    \n
    import { createLazyFileRoute, Link } from "@tanstack/react-router";\n\nexport const Route = createLazyFileRoute("/")({\n  component: Index,\n});\n\nfunction Index() {\n  return (\n    <div className="index">\n      <div className="index-brand">\n        <h1>Padre Gino's</h1>\n        <p>Pizza & Art at a location near you</p>\n      </div>\n      <ul>\n        <li>\n          <Link to="/order">Order</Link>\n        </li>\n        <li>\n          <Link to="/past">Past Orders</Link>\n        </li>\n      </ul>\n    </div>\n  );\n}\n
      \n
    • Nothing new here except maybe the links. These link components make it easy for TanStack Router to control the browser history so definitely use them.
    • \n
    \n

    Lastly let's modify Header to be able to link back to the home page.

    \n
    // at the top\nimport { Link } from "@tanstack/react-router";\n\n// surround the logo with a link\n<Link to={"/"}>\n  <h1 className="logo">Padre Gino's Pizza</h1>\n</Link>\n

    This should all work now. Open your app and see if it loads.

    \n
      \n
    • 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.
    • \n
    • 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.
    • \n
    • 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.
    • \n
    \n

    You did it! That's how easy routing has become. It's come so far from the early days of React.

    \n
    \n

    🏁 Click here to see the state of the project up until now: 09-routing

    \n
    \n","markdown":"\n## Tanstack Router\n\nSo 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.\n\nHistorically 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.\n\n## TanStack Router\n\nToday we are going to be using [TanStack Router][tsr], another amazing router. Why the switch? Two reasons:\n\n- 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).\n- In Intermediate React we will be covering more full-stack use cases, in which I will using Next.js to show off those features.\n\nSo, 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.\n\nLet's start by installing it.\n\n```bash\nnpm install @tanstack/react-router@1.65.0\nnpm install -D @tanstack/router-plugin@1.65.0 @tanstack/router-devtools@1.65.0\n```\n\nWe 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.\n\n```javascript\n// at top\nimport { TanStackRouterVite } from \"@tanstack/router-plugin/vite\";\n\n// add before react() in plugins\nplugins: [TanStackRouterVite(), react()],\n```\n\nTanStack Router works by file-based conventions. You create routes in a specific way and TanStack Router will automatically glue it together for you. It does this by creating a file called `routeTree.gen.ts`. Even though this isn't a TypeScript project, the fact that this is TypeScript means that VS Code can read the types from your routes and help you with suggestions and intelligent errors. I would suggest adding this file to your `.gitignore` as well since it will get autogenerated with every build. Okay, so let's go make the files we need.\n\nCreate a `routes` directory in your `src` directory. Let's make a `__root.jsx` file in there. This file will be the base template used for every route. Most of this will come from App.jsx\n\n```javascript\nimport { useState } from \"react\";\nimport { createRootRoute, Outlet } from \"@tanstack/react-router\";\nimport { TanStackRouterDevtools } from \"@tanstack/router-devtools\";\nimport PizzaOfTheDay from \"../PizzaOfTheDay\";\nimport Header from \"../Header\";\nimport { CartContext } from \"../contexts\";\n\nexport const Route = createRootRoute({\n component: () => {\n const cartHook = useState([]);\n return (\n <>\n \n
    \n
    \n \n \n
    \n
    \n \n \n );\n },\n});\n```\n\n- We added an `` instead of our Order component. Now we can swap in new routes there! Notice that the Header will always be there as will the PizzaOfTheDay.\n- We added TanStack's excellent dev tools. We'll take a look at them in a bit, but they're in the bottom left, you can click on them once your app loads.\n- `<>` and `` are for when you want to render two sibling components (our context and our dev tools) but don't want to add a random div there. React requires you only return one top level component and we can do that with `<>` and ``\n\nGreat, let's go modify App.jsx now\n\n```javascript\n// add at top\n// remove useState import from react import\nimport { RouterProvider, createRouter } from \"@tanstack/react-router\";\nimport { routeTree } from \"./routeTree.gen\";\n\nconst router = createRouter({ routeTree });\n\n// replace App\nconst App = () => {\n return (\n \n \n \n );\n};\n```\n\nThis just imports the generated routeTree and then makes use of it in the project. This file really only should be used for rendering the file. Everything else should likely live in \\_\\_root.jsx.\n\nOkay, move Order.jsx from the base directory and into `routes`. If it asks, say yes to update paths. Rename it to `order.lazy.jsx` Let's modify it now to make it a route.\n\n```javascript\n// at top\nimport { createLazyFileRoute } from \"@tanstack/react-router\";\n\n// make sure you modified the relative paths here – VS Code may have done this for you already\nimport { CartContext } from \"../contexts\";\nimport Cart from \"../Cart\";\nimport Pizza from \"../Pizza\";\n\nexport const Route = createLazyFileRoute(\"/order\")({\n component: Order,\n});\n\nfunction Order() {\n // Order component code …\n}\n```\n\n- Here we're making a new route. We define what URL it's at, `/order`, and what component to render, `Order`.\n- We're making it lazy. It will now code split this for us and lazy-load our route for us. This really helps in large apps. In this tiny app it probably actually slows us down because adding 1KB to an app doesn't slow it down, but an extra round trip does. When you do lazy loading, please measure if it's helping. In any case, this is how you do it.\n\nThat's it! This used to be so hard to code split. In some of my old classes we had to go through some serious work to get it work but now this is all it takes! Pretty cool.\n\nLet's add a home page! Make a file called `index.lazy.jsx` in the routes folder.\n\n```javascript\nimport { createLazyFileRoute, Link } from \"@tanstack/react-router\";\n\nexport const Route = createLazyFileRoute(\"/\")({\n component: Index,\n});\n\nfunction Index() {\n return (\n
    \n
    \n

    Padre Gino's

    \n

    Pizza & Art at a location near you

    \n
    \n
      \n
    • \n Order\n
    • \n
    • \n Past Orders\n
    • \n
    \n
    \n );\n}\n```\n\n- Nothing new here except maybe the links. These link components make it easy for TanStack Router to control the browser history so definitely use them.\n\nLastly let's modify Header to be able to link back to the home page.\n\n```javascript\n// at the top\nimport { Link } from \"@tanstack/react-router\";\n\n// surround the logo with a link\n\n

    Padre Gino's Pizza

    \n\n```\n\nThis should all work now. Open your app and see if it loads.\n\n- 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.\n- 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.\n- 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.\n\nYou did it! That's how easy routing has become. It's come so far from the early days of React.\n\n> 🏁 [Click here to see the state of the project up until now: 09-routing][step]\n\n[step]: https://github.com/btholt/citr-v9-project/tree/master/09-routing\n[rr]: https://reactrouter.com/\n[v8]: https://react-v8.holt.courses/lessons/react-capabilities/react-router\n[tsr]: https://tanstack.com/router/latest\n","slug":"tanstack-router","title":"TanStack Router","section":"Ecosystem","icon":"seedling","filePath":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/05-ecosystem/A-tanstack-router.md","nextSlug":"/lessons/ecosystem/tanstack-query","prevSlug":"/lessons/core-react-concepts/context"}},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/no-frills-react/components.json b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/no-frills-react/components.json new file mode 100644 index 0000000..105e2c2 --- /dev/null +++ b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/no-frills-react/components.json @@ -0,0 +1 @@ +{"pageProps":{"post":{"attributes":{"description":"Learn how to separate React components into distinct files and make reusable, flexible components that accept props, transforming a static app into a dynamic one. This guide provides code examples in creating a React app featuring customizable Pizza components, enhancing React development skills. Discover these strategies in Brian Holt's Complete Intro to React, ideal for budding web developers.","keywords":["React components","JavaScript","web development","reusable components","dynamic apps"]},"html":"

    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.)

    \n

    Modify your code in App.js so it looks like:

    \n
    const Pizza = () => {\n  return React.createElement("div", {}, [\n    React.createElement("h1", {}, "The Pepperoni Pizza"),\n    React.createElement("p", {}, "Mozzarella Cheese, Pepperoni"),\n  ]);\n};\n\nconst App = () => {\n  return React.createElement("div", {}, [\n    React.createElement("h1", {}, "Pixel Perfect Pizzas"),\n    React.createElement(Pizza),\n    React.createElement(Pizza),\n    React.createElement(Pizza),\n  ]);\n};\n\nconst container = document.getElementById("root");\nconst root = ReactDOM.createRoot(container);\nroot.render(React.createElement(App));\n
    \n

    🚨 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.

    \n
    \n
      \n
    • To make an element have multiple children, just pass it an array of elements.
    • \n
    • 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.
    • \n
    • Since we have a new Pizza component, we can use it multiple times! We just use multiple calls to React.createElement.
    • \n
    • 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.
    • \n
    \n

    Okay so we can have multiple pizzas but it's not a useful component yet since not all pizza will be a pepperoni pizza. Let's make it a bit more complicated.

    \n
    const Pizza = (props) => {\n  return React.createElement("div", {}, [\n    React.createElement("h1", {}, props.name),\n    React.createElement("p", {}, props.description),\n  ]);\n};\n\nconst App = () => {\n  return React.createElement("div", {}, [\n    React.createElement("h1", {}, "Pixel Perfect Pizzas"),\n    React.createElement(Pizza, {\n      name: "The Pepperoni Pizza",\n      description: "Mozzarella Cheese, Pepperoni",\n    }),\n    React.createElement(Pizza, {\n      name: "The Hawaiian Pizza",\n      description: "Sliced Ham, Pineapple, Mozzarella Cheese",\n    }),\n    React.createElement(Pizza, {\n      name: "The Big Meat Pizza",\n      description: "Bacon, Pepperoni, Italian Sausage, Chorizo Sausage",\n    }),\n  ]);\n};\n\nconst container = document.getElementById("root");\nconst root = ReactDOM.createRoot(container);\nroot.render(React.createElement(App));\n

    Now we have a more flexible component that accepts props from its parent. Props are variables that a parent (App) passes to its children (the instances of Pizza.) Now each one can be different! Now that is far more useful than it was since this Pizza component can represent not just a pepperoni, but any Pizza. This is the power of React! We can make multiple, re-usable components. We can then use these components to build larger components, which in turn make up yet-larger components. This is how React apps are made!

    \n
    \n

    🏁 Click here to see the state of the project up until now: 01-no-frills-react

    \n
    \n","markdown":"\nNow that we've done that, let's separate this out from a script tag on the DOM to its own script file (best practice.)\n\nModify your code in `App.js` so it looks like:\n\n```javascript\nconst Pizza = () => {\n return React.createElement(\"div\", {}, [\n React.createElement(\"h1\", {}, \"The Pepperoni Pizza\"),\n React.createElement(\"p\", {}, \"Mozzarella Cheese, Pepperoni\"),\n ]);\n};\n\nconst App = () => {\n return React.createElement(\"div\", {}, [\n React.createElement(\"h1\", {}, \"Pixel Perfect Pizzas\"),\n React.createElement(Pizza),\n React.createElement(Pizza),\n React.createElement(Pizza),\n ]);\n};\n\nconst container = document.getElementById(\"root\");\nconst root = ReactDOM.createRoot(container);\nroot.render(React.createElement(App));\n```\n\n> 🚨 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.\n\n- To make an element have multiple children, just pass it an array of elements.\n- 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.\n- Since we have a new `Pizza` component, we can use it multiple times! We just use multiple calls to `React.createElement`.\n- 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.\n\nOkay so we can have multiple pizzas but it's not a useful component yet since not all pizza will be a pepperoni pizza. Let's make it a bit more complicated.\n\n```javascript\nconst Pizza = (props) => {\n return React.createElement(\"div\", {}, [\n React.createElement(\"h1\", {}, props.name),\n React.createElement(\"p\", {}, props.description),\n ]);\n};\n\nconst App = () => {\n return React.createElement(\"div\", {}, [\n React.createElement(\"h1\", {}, \"Pixel Perfect Pizzas\"),\n React.createElement(Pizza, {\n name: \"The Pepperoni Pizza\",\n description: \"Mozzarella Cheese, Pepperoni\",\n }),\n React.createElement(Pizza, {\n name: \"The Hawaiian Pizza\",\n description: \"Sliced Ham, Pineapple, Mozzarella Cheese\",\n }),\n React.createElement(Pizza, {\n name: \"The Big Meat Pizza\",\n description: \"Bacon, Pepperoni, Italian Sausage, Chorizo Sausage\",\n }),\n ]);\n};\n\nconst container = document.getElementById(\"root\");\nconst root = ReactDOM.createRoot(container);\nroot.render(React.createElement(App));\n```\n\nNow we have a more flexible component that accepts props from its parent. Props are variables that a parent (App) passes to its children (the instances of Pizza.) Now each one can be different! Now that is far more useful than it was since this Pizza component can represent not just a pepperoni, but any Pizza. This is the power of React! We can make multiple, re-usable components. We can then use these components to build larger components, which in turn make up yet-larger components. This is how React apps are made!\n\n> 🏁 [Click here to see the state of the project up until now: 01-no-frills-react][step]\n\n[step]: https://github.com/btholt/citr-v9-project/tree/master/01-no-frills-react\n","slug":"components","title":"Components","section":"No Frills React","icon":"eye","filePath":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/02-no-frills-react/B-components.md","nextSlug":"/lessons/tools/npm","prevSlug":"/lessons/no-frills-react/react-without-a-build-step"}},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/no-frills-react/react-without-a-build-step.json b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/no-frills-react/react-without-a-build-step.json new file mode 100644 index 0000000..aa6b58b --- /dev/null +++ b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/no-frills-react/react-without-a-build-step.json @@ -0,0 +1 @@ +{"pageProps":{"post":{"attributes":{"description":"Learn the basics of creating a simple React app without using tools like Babel or Webpack. This tutorial guides you through setting up a project directory, writing pure JavaScript with React, and creating components to build a pizza ordering system. Ideal for beginners, this step-by-step guide by Brian Holt from the Complete Intro to React v9 course helps you understand fundamental React concepts using only essential tools.","keywords":["React","JavaScript","tutorial","beginner","Brian Holt","web development"]},"html":"

    Let's start by writing pure React. No compile step. No JSX. No Babel. No Webpack or Vite. Just some JavaScript on a page.

    \n

    Let's start your project. Create your project directory inside the repo. I'm going to call mine padre-ginos 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:

    \n
    <!DOCTYPE html>\n<html lang="en">\n  <head>\n    <meta charset="UTF-8" />\n    <meta name="viewport" content="width=device-width, initial-scale=1.0" />\n    <title>Padre Gino's</title>\n  </head>\n\n  <body>\n    <div id="root">not rendered</div>\n    <script src="https://unpkg.com/react@18.3.1/umd/react.development.js"></script>\n    <script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js"></script>\n    <script src="./src/App.js"></script>\n  </body>\n</html>\n

    Let's run this. We could open it directly in our browser but I like using serve. Run npx serve and open http://localhost:3000/ in your browser.

    \n
      \n
    • Pretty standard HTML5 document. If this is confusing, I teach another course called Intro to Web Dev that can help you out.
    • \n
    • 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.
    • \n
    • We have two script tags.
        \n
      • The first is the React library. This library is the interface of how to interact with React; all the methods (except one) will be via this library. It contains no way of rendering itself though; it's just the API.
      • \n
      • The second library is the rendering layer. Since we're rendering to the browser, we're using React DOM. There are other React libraries like React Native, React Unity, React Babylon.js, React Email, React Figma, React Blessed, and others. You need both script tags. The order is not important.
      • \n
      \n
    • \n
    • The last script tag is where we're going to put our code. You don't typically do this but I wanted to start as simple as possible. This script tag must come after the other two.
    • \n
    \n
    \n

    If you want to add some CSS right now, click here to get the stylesheet for this course. Make a file called styles.css and paste the previous file there. Then add a link tag to your html file <link rel="stylesheet" href="./style.css" />. We'll be linking to a CSS file located at /api/pubic/styles.css a little later in the course, but before we do that, we need to do some configuration (during the Vite lesson).

    \n
    \n

    Make a new directory called src and a new file called App.js in that directory and put this in there.

    \n
    const App = () => {\n  return React.createElement(\n    "div",\n    {},\n    React.createElement("h1", {}, "Pixel Perfect Pizzas")\n  );\n};\n\nconst container = document.getElementById("root");\nconst root = ReactDOM.createRoot(container);\nroot.render(React.createElement(App));\n

    This is about the simplest React app you can build.

    \n
      \n
    • The first thing we do is make our own component, App. React is all about making components. And then taking those components and making more components out of those.
    • \n
    • There are two types of components, function components and class components. This is a function component. We'll see class components shortly.
    • \n
    • A function component must return markup (which is what React.createElement generates.)
    • \n
    • These component render functions have to be fast. This function is going to be called a lot. It's a hot code path.
    • \n
    • Inside of the render function, you cannot modify any sort of state. Put in functional terms, this function must be pure. You don't know how or when the function will be called so it can't modify any ambient state.
    • \n
    • React.createElement creates one instance of some component. If you pass it a string, it will create a DOM tag with that as the string. We used h1 and div, those tags are output to the DOM. If we put x-custom-date-picker, it'll output that (so web components are possible too.)
    • \n
    • The second empty object (you can put null too) is attributes we're passing to the tag or component. Whatever we put in this will be output to the element (like id or style.)
    • \n
    • First we're using document.getElementById to grab an existing div out of the HTML document. Then we take that element (which we called container) and pass that into ReactDOM.createRoot. This is how we signal to React where we want it to render our app. Note later we can root.render again to change what the root of our React app looks like (I rarely need to do that.)
    • \n
    • Notice we're using React.createElement with App as a parameter to root.render. We need an instance of App to render out. App is a class of components and we need to render one instance of a class. That's what React.createElement does: it makes an instance of a class. An analogy is that App as a class of components is like Honda has a line of cars called Civics. It's a whole line of cars with various different options and parameters. An instance of a Civic would be one individual car. It's a concrete instance of the Civic car line.
    • \n
    \n
    \n

    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.

    \n
    \n","markdown":"\nLet's start by writing pure React. No compile step. No JSX. No Babel. No Webpack or Vite. Just some JavaScript on a page.\n\nLet's start your project. Create your project directory inside the repo. I'm going to call mine `padre-ginos` 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:\n\n```html\n\n\n \n \n \n Padre Gino's\n \n\n \n
    not rendered
    \n \n \n \n \n\n```\n\nLet'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.\n\n- Pretty standard HTML5 document. If this is confusing, I teach another course called [Intro to Web Dev][webdev] that can help you out.\n- 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.\n- We have two script tags.\n - The first is the React library. This library is the interface of how to interact with React; all the methods (except one) will be via this library. It contains no way of rendering itself though; it's just the API.\n - The second library is the rendering layer. Since we're rendering to the browser, we're using React DOM. There are other React libraries like React Native, React Unity, React Babylon.js, React Email, React Figma, React Blessed, and others. You need both script tags. The order is not important.\n- The last script tag is where we're going to put our code. You don't typically do this but I wanted to start as simple as possible. This script tag must come _after_ the other two.\n\n> If you want to add some CSS right now, [click here][style] to get the stylesheet for this course. Make a file called styles.css and paste the previous file there. Then add a link tag to your html file ``. We'll be linking to a CSS file located at `/api/pubic/styles.css` a little later in the course, but before we do that, we need to do some configuration (during the Vite lesson).\n\nMake a new directory called `src` and a new file called `App.js` in that directory and put this in there.\n\n```javascript\nconst App = () => {\n return React.createElement(\n \"div\",\n {},\n React.createElement(\"h1\", {}, \"Pixel Perfect Pizzas\")\n );\n};\n\nconst container = document.getElementById(\"root\");\nconst root = ReactDOM.createRoot(container);\nroot.render(React.createElement(App));\n```\n\nThis is about the simplest React app you can build.\n\n- The first thing we do is make our own component, App. React is all about making components. And then taking those components and making more components out of those.\n- There are two types of components, function components and class components. This is a function component. We'll see class components shortly.\n- A function component _must_ return markup (which is what `React.createElement` generates.)\n- These component render functions _have_ to be fast. This function is going to be called a lot. It's a hot code path.\n- Inside of the render function, you cannot modify any sort of state. Put in functional terms, this function must be pure. You don't know how or when the function will be called so it can't modify any ambient state.\n- `React.createElement` creates one _instance_ of some component. If you pass it a _string_, it will create a DOM tag with that as the string. We used `h1` and `div`, those tags are output to the DOM. If we put `x-custom-date-picker`, it'll output that (so web components are possible too.)\n- The second empty object (you can put `null` too) is attributes we're passing to the tag or component. Whatever we put in this will be output to the element (like id or style.)\n- First we're using `document.getElementById` to grab an existing div out of the HTML document. Then we take that element (which we called `container`) and pass that into `ReactDOM.createRoot`. This is how we signal to React where we want it to render our app. Note later we can `root.render` again to change what the root of our React app looks like (I rarely need to do that.)\n- Notice we're using `React.createElement` with `App` as a parameter to `root.render`. We need an _instance_ of `App` to render out. `App` is a class of components and we need to render one instance of a class. That's what `React.createElement` does: it makes an instance of a class. An analogy is that `App` as a _class_ of components is like Honda has a line of cars called Civics. It's a whole line of cars with various different options and parameters. An _instance_ of a Civic would be one individual car. It's a concrete instance of the Civic car line.\n\n> 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.\n\n[webdev]: https://frontendmasters.com/courses/web-development-v3/\n[style]: https://github.com/btholt/citr-v9-project/blob/main/api/public/style.css\n[serve]: https://github.com/vercel/serve\n","slug":"react-without-a-build-step","title":"React without a Build Step","section":"No Frills React","icon":"eye","filePath":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/02-no-frills-react/A-react-without-a-build-step.md","nextSlug":"/lessons/no-frills-react/components","prevSlug":"/lessons/welcome/my-setup"}},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/testing/basic-react-tests.json b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/testing/basic-react-tests.json new file mode 100644 index 0000000..e2da086 --- /dev/null +++ b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/testing/basic-react-tests.json @@ -0,0 +1 @@ +{"pageProps":{"post":{"attributes":{"description":"Learn how to effectively test React components with a focus on functionality over implementation using React Testing Library, through this hands-on guide by Brian Holt. Gain insights into user-centric testing methodologies, writing bug-catching tests, and managing test lifecycle for enhanced app reliability, specifically applied to the Pizza.jsx component.","keywords":["React Testing","React","Vitest","Testing Library","React Components","Brian Holt","Pizza.jsx"]},"html":"

    Let's write our first test for Pizza.jsx. In general, here's my methodology for testing React:

    \n
      \n
    • Try to test functionality, not implementation. Make your tests interact with components as a user would, not as a developer would. This means you're trying to do more to think of things like "what would a user see" or "if a user clicks a button a modal comes up" rather than "make sure this state is correct" or "ensure this library is called". This isn't a rule; sometimes you need to test those things too for assurance the app is working correctly. Use your best judgment.
    • \n
    • Every UI I've ever worked on changes a lot. Try to not unnecessarily spin your wheels on things that aren't important and are likely to change.
    • \n
    • In general when I encounter a bug that is important for me to go back and fix, I'll write a test that would have caught that bug. Actually what I'll do is before I fix it, I'll write the test that fails. That way I fix it I'll know I won't regress back there.
    • \n
    • Ask yourself what's important about your app and spend your time testing that. Ask yourself "if a user couldn't do X then the app is worthless" sort of questions and test those more thoroughly. If a user can't change themes then it's probably not the end of the world (a11y is important) so you can spend less time testing that but if a user can't log in then the app is worthless. Test that.
    • \n
    • Delete tests on a regular basis. Tests have a shelf life.
    • \n
    • Fix or delete flaky tests. Bad tests are worse than no tests.
    • \n
    \n

    Okay, create a new file called Pizza.test.jsx. This naming convention is just habit. Pizza.spec.jsx is common too. But as long as it's in the __tests__ directory it doesn't much matter what you call it.

    \n
    import { render } from "@testing-library/react";\nimport { expect, test } from "vitest";\nimport Pizza from "../Pizza";\n\ntest("alt text renders on image", async () => {\n  const name = "My Favorite Pizza";\n  const src = "https://picsum.photos/200";\n  const screen = render(\n    <Pizza name={name} description="super cool pizza" image={src} />\n  );\n\n  const img = screen.getByRole("img");\n  expect(img.src).toBe(src);\n  expect(img.alt).toBe(name);\n});\n

    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.

    \n

    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.)

    \n

    Let's add another test to make sure that we have a default image if pizza isn't passed an image.

    \n
    \n

    🚨 This doesn't work yet. That's intentional.

    \n
    \n
    test("to have default image if none is provided", async () => {\n  const screen = render(\n    <Pizza name={"Cool Pizza"} description="super cool pizza" />\n  );\n\n  const img = screen.getByRole("img");\n  expect(img.src).not.toBe("");\n});\n

    Well, first thing it is going to complain about is that we don't clean up after ourselves after each test. React Testing Library requires us to.

    \n
    // at top\nimport { render, cleanup } from "@testing-library/react"; // add cleanup\nimport { afterEach, expect, test } from "vitest"; // add afterEach\nafterEach(cleanup);\n

    The test still doesn't pass? Oh, that's because it caught a bug! If you don't give it a image, it just breaks. That's not a good user experience. Let's go fix it in Pizza.jsx.

    \n
    // replace src\nsrc={props.image ? props.image : "https://picsum.photos/200"}\n

    Now it works!

    \n

    Bam! Some easy React testing there for you.

    \n","markdown":"\nLet's write our first test for Pizza.jsx. In general, here's my methodology for testing React:\n\n- Try to test functionality, not implementation. Make your tests interact with components as a user would, not as a developer would. This means you're trying to do more to think of things like \"what would a user see\" or \"if a user clicks a button a modal comes up\" rather than \"make sure this state is correct\" or \"ensure this library is called\". This isn't a rule; sometimes you need to test those things too for assurance the app is working correctly. Use your best judgment.\n- Every UI I've ever worked on changes a lot. Try to not unnecessarily spin your wheels on things that aren't important and are likely to change.\n- In general when I encounter a bug that is important for me to go back and fix, I'll write a test that would have caught that bug. Actually what I'll do is _before_ I fix it, I'll write the test that fails. That way I fix it I'll know I won't regress back there.\n- Ask yourself what's important about your app and spend your time testing that. Ask yourself \"if a user couldn't do X then the app is worthless\" sort of questions and test those more thoroughly. If a user can't change themes then it's probably not the end of the world (a11y is important) so you can spend less time testing that but if a user can't log in then the app is worthless. Test that.\n- Delete tests on a regular basis. Tests have a shelf life.\n- Fix or delete flaky tests. Bad tests are worse than no tests.\n\nOkay, create a new file called `Pizza.test.jsx`. This naming convention is just habit. `Pizza.spec.jsx` is common too. But as long as it's in the `__tests__` directory it doesn't much matter what you call it.\n\n```javascript\nimport { render } from \"@testing-library/react\";\nimport { expect, test } from \"vitest\";\nimport Pizza from \"../Pizza\";\n\ntest(\"alt text renders on image\", async () => {\n const name = \"My Favorite Pizza\";\n const src = \"https://picsum.photos/200\";\n const screen = render(\n \n );\n\n const img = screen.getByRole(\"img\");\n expect(img.src).toBe(src);\n expect(img.alt).toBe(name);\n});\n```\n\nThis 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.\n\nWe'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.)\n\nLet's add another test to make sure that we have a default image if pizza isn't passed an image.\n\n> 🚨 This doesn't work yet. That's intentional.\n\n```javascript\ntest(\"to have default image if none is provided\", async () => {\n const screen = render(\n \n );\n\n const img = screen.getByRole(\"img\");\n expect(img.src).not.toBe(\"\");\n});\n```\n\nWell, first thing it is going to complain about is that we don't clean up after ourselves after each test. React Testing Library requires us to.\n\n```javascript\n// at top\nimport { render, cleanup } from \"@testing-library/react\"; // add cleanup\nimport { afterEach, expect, test } from \"vitest\"; // add afterEach\nafterEach(cleanup);\n```\n\nThe test still doesn't pass? Oh, that's because it caught a bug! If you don't give it a image, it just breaks. That's not a good user experience. Let's go fix it in Pizza.jsx.\n\n```javascript\n// replace src\nsrc={props.image ? props.image : \"https://picsum.photos/200\"}\n```\n\nNow it works!\n\nBam! Some easy React testing there for you.\n","slug":"basic-react-tests","title":"Basic React Tests","section":"Testing","icon":"vial","filePath":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/07-testing/B-basic-react-tests.md","nextSlug":"/lessons/testing/testing-user-interaction","prevSlug":"/lessons/testing/vitest"}},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/testing/browser-tests.json b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/testing/browser-tests.json new file mode 100644 index 0000000..ad6324e --- /dev/null +++ b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/testing/browser-tests.json @@ -0,0 +1 @@ +{"pageProps":{"post":{"attributes":{"description":"Explore the emerging support for browser-based testing with Vitest, featuring Playwright integration for fast and reliable testing. Learn how to configure Vitest for both Node.js and browser environments, leveraging libraries like vitest-browser-react, and gain insights from Brian Holt on crafting effective tests for user experiences in React applications. This guide is ideal for developers keen on improving their testing setups in modern web development environments.","keywords":["Vitest","Playwright","browser-based testing","React testing","vitest-browser-react","Brian Holt","web development"]},"html":"
    \n

    🚨 This is experimental and very likely to change in the future. Consider this a sneak peak into what is coming, not what to do today.

    \n
    \n

    Vitest is beginning to support more deeply browser-based testing. To those of us that have been around long enough to remember Selenium, this may strike fear deep int your heart. But fear not! Browser-based testing tools have come so far along since then that it's both fast and reliable, in particular thanks to the Microsoft project Playwright.

    \n

    However this is still early days. I'm going to show you how to set it up but be warned that this prone to change as they're still actively working on it!

    \n

    So first let's install the libraries we need.

    \n
    npm i -D @vitest/browser@2.1.3 playwright@1.48.0 vitest-browser-react@0.0.1\n

    You can see that the vitest-browser-react library is still 0.0.1 as of writing so be extra aware it's likely to have changed by the time you read this.

    \n

    Okay, so now we want to write browser based tests. But we have also have a bunch of existing Node.js-based tests. Some our already-existing Node.js based tests won't work in the browser. But no worries, Vitest/Vite has a tool just for this, workspaces. It's actually made to handle monorepos, but it will work here just as well. Make a file called vitest.workspace.js (you don't normally need this as your Vite config is normally enough.)

    \n
    import { defineWorkspace } from "vitest/config";\n\nexport default defineWorkspace([\n  {\n    extends: "./vite.config.js",\n    test: {\n      include: ["**/*.node.test.{js,jsx}"],\n      name: "happy-dom",\n      environment: "happy-dom",\n    },\n  },\n  {\n    extends: "./vite.config.js",\n    test: {\n      setupFiles: ["vitest-browser-react"],\n      include: ["**/*.browser.test.{js,jsx}"],\n      name: "browser",\n      browser: {\n        provider: "playwright",\n        enabled: true,\n        name: "firefox", // you can use chromium or webkit here too\n      },\n    },\n  },\n]);\n

    Now delete the test items from your vite.config.js file.

    \n

    This is a test-only config for Vite (and therefore Vitest.) Now if a test ends in .node.test.jsx it will run through the happy-dom based environment and if it ends in .browser.test.jsx it will run in our new browser-based environment with Playwright. Let's go rename our tests to reflect that.

    \n
      \n
    • Cart.browser.test.jsx
    • \n
    • contact.lazy.node.test.jsx
    • \n
    • Pizza.node.test.jsx
    • \n
    • usePizzaOfTheDay.node.test.jsx
    • \n
    \n

    Snapshotting works in the browser so that one works okay. Anything using vitest-fetch-mock is Node.js only so for those we need to mark them as node. We're going to make a new Pizza file so let's leave that one. And our custom hook test mocks fetch so that one is Node only.

    \n

    Okay, now create a Pizza.browser.test.jsx

    \n
    import { render } from "vitest-browser-react";\nimport { expect, test } from "vitest";\nimport Pizza from "../Pizza";\n\ntest("alt text renders on image", async () => {\n  const name = "My Favorite Pizza";\n  const src = "https://picsum.photos/200";\n  const screen = render(\n    <Pizza name={name} description="super cool pizza" image={src} />\n  );\n\n  const img = await screen.getByRole("img");\n\n  await expect.element(img).toBeInTheDocument();\n  await expect.element(img).toHaveAttribute("src", src);\n  await expect.element(img).toHaveAttribute("alt", name);\n});\n
      \n
    • render will take a React component and render it in a vacuum. You can then poke and prod it as you need to test it.
    • \n
    • A big part of what Playwright and vitest-browser-react want you to do is not test implementation details but to test actual user experiences. Don't test the internal state of a React component but do test what users will see and experience. As such, a lot of we'll be testing will be around roles, attributes, etc.
    • \n
    • @testing-library has a good doc on why they choose to test this way.
    • \n
    • In general, vitest-browser-react aims to be a drop in replacement for @testing-library/react.
    • \n
    \n

    Looks really similar, right? Alright, let's run it. npm run test. You should see the same Vitest UI but now some of the tests are actually running in the browser.

    \n
    \n

    It will likely prompt you to run a command like npx playwright install. You'll install local copies of browsers to able to run them super fast.

    \n
    \n

    Cool, right? And really fast! Let's do one more. Let's make a Header.jsx test. We're going to test that the cart number is correct. Remember when Facebook notification numbers were always wrong? We're going to make sure that doesn't happen with our cart indicator. In your Header.jsx file:

    \n
    data-testid="cart-number" // add to .nav-cart-number\n

    Now make a Header.browser.test.jsx

    \n
    import { render } from "vitest-browser-react";\nimport { expect, test } from "vitest";\nimport Header from "../Header";\nimport {\n  RouterProvider,\n  createRouter,\n  createRootRoute,\n} from "@tanstack/react-router";\nimport { CartContext } from "../contexts";\n\ntest("correctly renders a header with a zero cart count", async () => {\n  const rootRoute = createRootRoute({\n    component: () => (\n      <CartContext.Provider value={[[]]}>\n        <Header />\n      </CartContext.Provider>\n    ),\n  });\n\n  const router = createRouter({ routeTree: rootRoute });\n  const screen = render(<RouterProvider router={router}></RouterProvider>);\n\n  const itemsInCart = await screen.getByTestId("cart-number");\n\n  await expect.element(itemsInCart).toBeInTheDocument();\n  await expect.element(itemsInCart).toHaveTextContent("0");\n});\n\ntest("correctly renders a header with a three cart count", async () => {\n  const rootRoute = createRootRoute({\n    component: () => (\n      <CartContext.Provider\n        value={[[{ pizza: 1 }, { pizza: 2 }, { pizza: 3 }]]}\n      >\n        <Header />\n      </CartContext.Provider>\n    ),\n  });\n\n  const router = createRouter({ routeTree: rootRoute });\n  const screen = render(<RouterProvider router={router}></RouterProvider>);\n\n  const itemsInCart = await screen.getByTestId("cart-number");\n\n  await expect.element(itemsInCart).toBeInTheDocument();\n  await expect.element(itemsInCart).toHaveTextContent("3");\n});\n

    We do have to bend over a bit backwards to make sure TanStack Router is happy, hence all the making of root routes. Remember also that our cart gets its cart from context so we have to pass it in that way. Beyond that, it works very similar!

    \n

    Again, these are early days for browser-based testing with Vite so proceed in your professional settings with caution. However the future is bright with Playwright!

    \n
    \n

    🏁 Click here to see the state of the project up until now: 14-testing

    \n
    \n","markdown":"\n> 🚨 This is experimental and very likely to change in the future. Consider this a sneak peak into what is coming, not what to do today.\n\nVitest is beginning to support more deeply browser-based testing. To those of us that have been around long enough to remember Selenium, this may strike fear deep int your heart. But fear not! Browser-based testing tools have come so far along since then that it's both fast and reliable, in particular thanks to the Microsoft project [Playwright][playwright].\n\nHowever this is still early days. I'm going to show you how to set it up but be warned that this prone to change as they're still actively working on it!\n\nSo first let's install the libraries we need.\n\n```bash\nnpm i -D @vitest/browser@2.1.3 playwright@1.48.0 vitest-browser-react@0.0.1\n```\n\nYou can see that the vitest-browser-react library is still 0.0.1 as of writing so be extra aware it's likely to have changed by the time you read this.\n\nOkay, so now we want to write browser based tests. But we have also have a bunch of existing Node.js-based tests. Some our already-existing Node.js based tests won't work in the browser. But no worries, Vitest/Vite has a tool just for this, workspaces. It's actually made to handle monorepos, but it will work here just as well. Make a file called `vitest.workspace.js` (you don't normally need this as your Vite config is normally enough.)\n\n```javascript\nimport { defineWorkspace } from \"vitest/config\";\n\nexport default defineWorkspace([\n {\n extends: \"./vite.config.js\",\n test: {\n include: [\"**/*.node.test.{js,jsx}\"],\n name: \"happy-dom\",\n environment: \"happy-dom\",\n },\n },\n {\n extends: \"./vite.config.js\",\n test: {\n setupFiles: [\"vitest-browser-react\"],\n include: [\"**/*.browser.test.{js,jsx}\"],\n name: \"browser\",\n browser: {\n provider: \"playwright\",\n enabled: true,\n name: \"firefox\", // you can use chromium or webkit here too\n },\n },\n },\n]);\n```\n\nNow delete the `test` items from your vite.config.js file.\n\nThis is a test-only config for Vite (and therefore Vitest.) Now if a test ends in `.node.test.jsx` it will run through the happy-dom based environment and if it ends in `.browser.test.jsx` it will run in our new browser-based environment with Playwright. Let's go rename our tests to reflect that.\n\n- Cart.browser.test.jsx\n- contact.lazy.node.test.jsx\n- Pizza.node.test.jsx\n- usePizzaOfTheDay.node.test.jsx\n\nSnapshotting works in the browser so that one works okay. Anything using vitest-fetch-mock is Node.js only so for those we need to mark them as node. We're going to make a new Pizza file so let's leave that one. And our custom hook test mocks fetch so that one is Node only.\n\nOkay, now create a Pizza.browser.test.jsx\n\n```javascript\nimport { render } from \"vitest-browser-react\";\nimport { expect, test } from \"vitest\";\nimport Pizza from \"../Pizza\";\n\ntest(\"alt text renders on image\", async () => {\n const name = \"My Favorite Pizza\";\n const src = \"https://picsum.photos/200\";\n const screen = render(\n \n );\n\n const img = await screen.getByRole(\"img\");\n\n await expect.element(img).toBeInTheDocument();\n await expect.element(img).toHaveAttribute(\"src\", src);\n await expect.element(img).toHaveAttribute(\"alt\", name);\n});\n```\n\n- `render` will take a React component and render it in a vacuum. You can then poke and prod it as you need to test it.\n- A big part of what Playwright and vitest-browser-react want you to do is not test implementation details but to test actual user experiences. Don't test the internal state of a React component but do test what users will see and experience. As such, a lot of we'll be testing will be around roles, attributes, etc.\n- [@testing-library][principles] has a good doc on why they choose to test this way.\n- In general, vitest-browser-react aims to be a drop in replacement for @testing-library/react.\n\nLooks really similar, right? Alright, let's run it. `npm run test`. You should see the same Vitest UI but now some of the tests are actually running in the browser.\n\n> It will likely prompt you to run a command like `npx playwright install`. You'll install local copies of browsers to able to run them super fast.\n\nCool, right? And really fast! Let's do one more. Let's make a Header.jsx test. We're going to test that the cart number is correct. Remember when Facebook notification numbers were always wrong? We're going to make sure that doesn't happen with our cart indicator. In your Header.jsx file:\n\n```javascript\ndata-testid=\"cart-number\" // add to .nav-cart-number\n```\n\nNow make a Header.browser.test.jsx\n\n```javascript\nimport { render } from \"vitest-browser-react\";\nimport { expect, test } from \"vitest\";\nimport Header from \"../Header\";\nimport {\n RouterProvider,\n createRouter,\n createRootRoute,\n} from \"@tanstack/react-router\";\nimport { CartContext } from \"../contexts\";\n\ntest(\"correctly renders a header with a zero cart count\", async () => {\n const rootRoute = createRootRoute({\n component: () => (\n \n
    \n \n ),\n });\n\n const router = createRouter({ routeTree: rootRoute });\n const screen = render();\n\n const itemsInCart = await screen.getByTestId(\"cart-number\");\n\n await expect.element(itemsInCart).toBeInTheDocument();\n await expect.element(itemsInCart).toHaveTextContent(\"0\");\n});\n\ntest(\"correctly renders a header with a three cart count\", async () => {\n const rootRoute = createRootRoute({\n component: () => (\n \n
    \n \n ),\n });\n\n const router = createRouter({ routeTree: rootRoute });\n const screen = render();\n\n const itemsInCart = await screen.getByTestId(\"cart-number\");\n\n await expect.element(itemsInCart).toBeInTheDocument();\n await expect.element(itemsInCart).toHaveTextContent(\"3\");\n});\n```\n\nWe do have to bend over a bit backwards to make sure TanStack Router is happy, hence all the making of root routes. Remember also that our cart gets its cart from context so we have to pass it in that way. Beyond that, it works very similar!\n\nAgain, these are early days for browser-based testing with Vite so proceed in your professional settings with caution. However the future is bright with Playwright!\n\n> 🏁 [Click here to see the state of the project up until now: 14-testing][step]\n\n[step]: https://github.com/btholt/citr-v9-project/tree/master/14-testing\n[principles]: https://testing-library.com/docs/guiding-principles\n[playwright]: https://playwright.dev/\n","slug":"browser-tests","title":"Browser Tests","section":"Testing","icon":"vial","filePath":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/07-testing/H-browser-tests.md","nextSlug":"/lessons/whats-next/react-19","prevSlug":"/lessons/testing/vitest-ui"}},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/testing/coverage.json b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/testing/coverage.json new file mode 100644 index 0000000..10d7ebf --- /dev/null +++ b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/testing/coverage.json @@ -0,0 +1 @@ +{"pageProps":{"post":{"attributes":{"description":"Learn how to enhance your React test coverage with Vitest and v8 in the Complete Intro to React v9 course by Brian Holt. This guide explains setting up Chrome's built-in test coverage tool, v8, to visualize code coverage and improve your test effectiveness, with alternative support for Istanbul. Perfect for React developers aiming to optimize their testing strategies.","title":"v8","keywords":["React","Vitest","v8","test coverage","Brian Holt","Istanbul","frontend development"]},"html":"

    One last very cool trick that Vitest has built into it: v8. v8 uses Chrome's builtin test coverage tool which tells you how much of your code that you're covering with tests. Via an interactive viewer you can see what lines are and aren't covered. This used to be annoying to set up by Vitest just does it for you.

    \n

    Run the following

    \n
    npm i -D @vitest/coverage-v8\n
    \n

    You can also use Istanbul if you want to. npm i -D @vitest/coverage-istanbul will get you the package. But I'd say v8 is the superior tool.

    \n
    \n

    Now add this to your vite.config.js

    \n
    coverage: {\n  reporter: ["text", "json", "html"],\n},\n

    Add the following command to your npm scripts: "coverage": "vitest --coverage" and go ahead run npm run coverage and open the following file in your browser: open coverage/index.html.

    \n

    Here you can see the files we've written tests for. One file, postContact is missing two lines of coverage (click on the file name to see that): it's the line of reading back from the cache. It's a file we didn't write a test for but it got covered via our test for the contact page. It's missing a test to cover error cases which could be important. What if our contact API is down? This tool helps you look through your code and see what your tests aren't covering. Some of those could be fine and not worth the effort writing a test for. Others you'll look at and say "oh my god we need to cover that with tests yesterday."

    \n

    Lastly, add coverage/ to your .gitignore since this shouldn't be checked in.

    \n

    Istanbul

    \n

    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 tools work with it. You can tell Vitest to use Istanbul but unless you have a very specific reason to, just use v8.

    \n","markdown":"\nOne last very cool trick that Vitest has built into it: [v8][v8]. v8 uses Chrome's builtin test coverage tool which tells you _how much_ of your code that you're covering with tests. Via an interactive viewer you can see what lines are and aren't covered. This used to be annoying to set up by Vitest just does it for you.\n\nRun the following\n\n```bash\nnpm i -D @vitest/coverage-v8\n```\n\n> You can also use [Istanbul][istanbul] if you want to. `npm i -D @vitest/coverage-istanbul` will get you the package. But I'd say v8 is the superior tool.\n\nNow add this to your vite.config.js\n\n```javascript\ncoverage: {\n reporter: [\"text\", \"json\", \"html\"],\n},\n```\n\nAdd the following command to your npm scripts: `\"coverage\": \"vitest --coverage\"` and go ahead run `npm run coverage` and open the following file in your browser: `open coverage/index.html`.\n\nHere you can see the files we've written tests for. One file, `postContact` is missing two lines of coverage (click on the file name to see that): it's the line of reading back from the cache. It's a file we didn't write a test for but it got covered via our test for the contact page. It's missing a test to cover error cases which could be important. What if our contact API is down? This tool helps you look through your code and see what your tests aren't covering. Some of those could be fine and not worth the effort writing a test for. Others you'll look at and say \"oh my god we need to cover that with tests yesterday.\"\n\nLastly, add `coverage/` to your `.gitignore` since this shouldn't be checked in.\n\n## Istanbul\n\nv8 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.\n\n[istanbul]: https://istanbul.js.org/\n[c8]: https://github.com/bcoe/c8\n[v8]: https://vitest.dev/guide/coverage.html\n","slug":"coverage","title":"v8","section":"Testing","icon":"vial","filePath":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/07-testing/F-coverage.md","nextSlug":"/lessons/testing/vitest-ui","prevSlug":"/lessons/testing/snapshot-testing"}},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/testing/snapshot-testing.json b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/testing/snapshot-testing.json new file mode 100644 index 0000000..1ec22a2 --- /dev/null +++ b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/testing/snapshot-testing.json @@ -0,0 +1 @@ +{"pageProps":{"post":{"attributes":{"description":"Discover snapshot testing in React with Brian Holt's Complete Intro to React, a course offering insights into efficient testing techniques for components like Cart.jsx. Learn about the benefits and limitations of snapshot tests, how to implement them using tools like Vitest, and explore their practical applications in UI and API development. Ideal for developers looking to enhance their testing strategies in web development.","keywords":["React","Snapshot Testing","Brian Holt","Vitest","UI Testing"]},"html":"

    I'm not a fan of seeking 100% test coverage. I think it's a fool's errand and a waste of time. I'd rather you write five tests that cover the most important five lines of your code than see you write one test for five less-important pieces of UI code.

    \n

    But let's show you an easy way to cheat and get there! Let's talk about snapshot testing.

    \n

    Snapshot tests are low confidence, low cost ways of writing tests. With more-or-less a single line of code you can assert: this code doesn't break, and it isn't changing over time.

    \n

    Let's test Cart.jsx. It's a pretty stable component that doesn't do a lot and we don't expect it to change a lot if at all. A low cost, low confidence test could fit here. Make a file called Cart.test.jsx

    \n
    import { expect, test } from "vitest";\nimport { render } from "@testing-library/react";\nimport Cart from "../Cart";\n\ntest("snapshot with nothing in cart", () => {\n  const { asFragment } = render(<Cart cart={[]} />);\n  expect(asFragment()).toMatchSnapshot();\n});\n

    Run this to see Vitest say it created a snapshot. Go look now at Cart.test.jsx.snap in the src/__tests__/__snapshots__ to see what it created. You can see it's just rendering out what it would look like. Now if you modify Cart.jsx it will fail the test. As you can see, it's a quick gut check to make sure your changes don't have cascading problems. If I modify App.jsx and it causes this to fail it means I can catch it and validate quickly. Some people don't find it useful. I'm not entirely sold on it to be honest; at most these should be used very sparingly. This could be useful if you have a component that you expect to never change and it would be a problem if it did. Maybe a footer? I don't know. I never write these.

    \n

    Let's add some pizzas and see how it does.

    \n
    test("snapshot with some stuff in cart", () => {\n  const { asFragment } = render(\n    <Cart\n      cart={[\n        {\n          pizza: {\n            id: "pepperoni",\n            name: "The Pepperoni Pizza",\n            category: "Classic",\n            description: "Mozzarella Cheese, Pepperoni",\n            image: "/public/pizzas/pepperoni.webp",\n            sizes: {\n              S: 9.75,\n              M: 12.5,\n              L: 15.25,\n            },\n          },\n          size: "M",\n          price: "$12.50",\n        },\n        {\n          pizza: {\n            id: "ckn_pesto",\n            name: "The Chicken Pesto Pizza",\n            category: "Chicken",\n            description:\n              "Chicken, Tomatoes, Red Peppers, Spinach, Garlic, Pesto Sauce",\n            image: "/public/pizzas/ckn_pesto.webp",\n            sizes: {\n              S: 12.75,\n              M: 16.75,\n              L: 20.75,\n            },\n          },\n          size: "L",\n          price: "$20.75",\n        },\n        {\n          pizza: {\n            id: "bbq_ckn",\n            name: "The Barbecue Chicken Pizza",\n            category: "Chicken",\n            description:\n              "Barbecued Chicken, Red Peppers, Green Peppers, Tomatoes, Red Onions, Barbecue Sauce",\n            image: "/public/pizzas/bbq_ckn.webp",\n            sizes: {\n              S: 12.75,\n              M: 16.75,\n              L: 20.75,\n            },\n          },\n          size: "S",\n          price: "$12.75",\n        },\n      ]}\n    />\n  );\n  expect(asFragment()).toMatchSnapshot();\n});\n

    This works, and now you can stick with this, but here's a problem. If you look at your snapshot, it's rendering Pizza components as well. Now if you modify Pizza.jsx (that has its own tests already) your Cart.jsx test is going to fail. This is misleading, nothing is wrong or different with Cart.jsx. In a previous version of this class I show you how to accomplish this with react-test-renderer. However the folks at @testing-lib think shallow rendering is more harmful than helpful (I like, 75% agree) so we'll leave it out. Click here to see me teach this previously. It's 95% the same, just uses Jest instead of Vitest.

    \n

    Update your snapshots by either running npm run test -- -u or you can use the watcher to do it with either u to update all at once or do i one-by-one.

    \n

    You should commit snapshot files to git.

    \n

    I'll leave it up to you how much you value these tests. I think they have a very limited place in UI testing but it's pretty low level of help. Frequently they become more noise than help. In any case, keep them in your toolbox, some times they can be helpful.

    \n
    \n

    As a side note, one place I saw some use for snapshot tests (as they can track any object shape over time, not just React components) was in the backend in API response shapes. We'd write a snapshot test that this API response is always going to look like this and it should fail the test if it breaks. This makes it very intentional every time you modify the API response (since your API clients are likely relying on it being a certain shape.) Furthermore, it means the frontend devs can use these snapshot files to see what the API response is going to look like. Niche, but it was helpful on the one project I worked on that had it.

    \n
    \n","markdown":"\nI'm not a fan of seeking 100% test coverage. I think it's a fool's errand and a waste of time. I'd rather you write five tests that cover the most important five lines of your code than see you write one test for five less-important pieces of UI code.\n\nBut let's show you an easy way to cheat and get there! Let's talk about snapshot testing.\n\nSnapshot tests are low confidence, low cost ways of writing tests. With more-or-less a single line of code you can assert: this code doesn't break, and it isn't changing over time.\n\nLet's test Cart.jsx. It's a pretty stable component that doesn't do a lot and we don't expect it to change a lot if at all. A low cost, low confidence test could fit here. Make a file called Cart.test.jsx\n\n```javascript\nimport { expect, test } from \"vitest\";\nimport { render } from \"@testing-library/react\";\nimport Cart from \"../Cart\";\n\ntest(\"snapshot with nothing in cart\", () => {\n const { asFragment } = render();\n expect(asFragment()).toMatchSnapshot();\n});\n```\n\nRun this to see Vitest say it created a snapshot. Go look now at Cart.test.jsx.snap in the `src/__tests__/__snapshots__` to see what it created. You can see it's just rendering out what it would look like. Now if you modify Cart.jsx it will fail the test. As you can see, it's a quick gut check to make sure your changes don't have cascading problems. If I modify App.jsx and it causes this to fail it means I can catch it and validate quickly. Some people don't find it useful. I'm not entirely sold on it to be honest; at most these should be used very sparingly. This could be useful if you have a component that you expect to _never_ change and it would be a problem if it did. Maybe a footer? I don't know. I never write these.\n\nLet's add some pizzas and see how it does.\n\n```javascript\ntest(\"snapshot with some stuff in cart\", () => {\n const { asFragment } = render(\n \n );\n expect(asFragment()).toMatchSnapshot();\n});\n```\n\nThis works, and now you can stick with this, but here's a problem. If you look at your snapshot, it's rendering Pizza components as well. Now if you modify Pizza.jsx (that has its own tests already) your _Cart.jsx_ test is going to fail. This is misleading, nothing is wrong or different with Cart.jsx. In a previous version of this class I show you how to accomplish this with react-test-renderer. However the folks at @testing-lib think shallow rendering is more harmful than helpful (I like, 75% agree) so we'll leave it out. [Click here][fem] to see me teach this previously. It's 95% the same, just uses Jest instead of Vitest.\n\nUpdate your snapshots by either running `npm run test -- -u` or you can use the watcher to do it with either `u` to update all at once or do `i` one-by-one.\n\nYou should commit snapshot files to git.\n\nI'll leave it up to you how much you value these tests. I think they have a very limited place in UI testing but it's pretty low level of help. Frequently they become more noise than help. In any case, keep them in your toolbox, some times they can be helpful.\n\n> As a side note, one place I saw some use for snapshot tests (as they can track any object shape over time, not just React components) was in the backend in API response shapes. We'd write a snapshot test that this API response is always going to look like this and it should fail the test if it breaks. This makes it very intentional every time you modify the API response (since your API clients are likely relying on it being a certain shape.) Furthermore, it means the frontend devs can use these snapshot files to see what the API response is going to look like. Niche, but it was helpful on the one project I worked on that had it.\n\n[fem]: https://frontendmasters.com/courses/intermediate-react-v4/snapshots/\n","slug":"snapshot-testing","title":"Snapshot Testing","section":"Testing","icon":"vial","filePath":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/07-testing/E-snapshot-testing.md","nextSlug":"/lessons/testing/coverage","prevSlug":"/lessons/testing/testing-custom-hooks"}},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/testing/testing-custom-hooks.json b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/testing/testing-custom-hooks.json new file mode 100644 index 0000000..0f677eb --- /dev/null +++ b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/testing/testing-custom-hooks.json @@ -0,0 +1 @@ +{"pageProps":{"post":{"attributes":{"description":"Learn how to test a custom React hook, usePizzaOfTheDay, by implementing a fake component and utilizing renderHook from testing-library/react. This approach ensures your hooks behave correctly, maintaining code integrity while interacting with APIs, like the example API call for \"Pizza of the Day.\"","keywords":["React testing","custom hooks","renderHook","testing-library","API testing"]},"html":"

    Let's say we needs tests for our custom hook, usePizzaOfTheDay. Testing custom hooks is a bit of a trick because they are inherently tied to the internal workings of React: they can't be called outside of a component. So how we do we get around that? We fake a component! Make a file called usePizzaOfTheDay.test.jsx in our __tests__ directory.

    \n
    import { expect, test, vi } from "vitest";\nimport { render } from "@testing-library/react";\nimport createFetchMock from "vitest-fetch-mock";\nimport { usePizzaOfTheDay } from "../usePizzaOfTheDay";\n\nconst fetchMocker = createFetchMock(vi);\nfetchMocker.enableMocks();\n\nconst testPizza = {\n  id: "calabrese",\n  name: "The Calabrese Pizza",\n  category: "Supreme",\n  description:\n    "Salami, Pancetta, Tomatoes, Red Onions, Friggitello Peppers, Garlic",\n  image: "/public/pizzas/calabrese.webp",\n  sizes: { S: 12.25, M: 16.25, L: 20.25 },\n};\n\nfunction getPizzaOfTheDay() {\n  let pizza;\n\n  function TestComponent() {\n    pizza = usePizzaOfTheDay();\n    return null;\n  }\n\n  render(<TestComponent />);\n\n  return pizza;\n}\n\ntest("gives null when first called", async () => {\n  fetch.mockResponseOnce(JSON.stringify(testPizza));\n  const pizza = getPizzaOfTheDay();\n  expect(pizza).toBeNull();\n});\n

    It's a little weird to implement a fake component to test something (we're dangerously close to the line of testing implementation details) but this is essentially library code and we want to assure ourselves this code works if we use it frequently in our code base.

    \n

    We can make this better though. Let's rewrite our test to look like this:

    \n
    import { renderHook } from "@testing-library/react"; // change import\n\ntest("to be null on initial load", async () => {\n  fetch.mockResponseOnce(JSON.stringify(testPizza));\n  const { result } = renderHook(() => usePizzaOfTheDay(""));\n  expect(result.current).toBeNull();\n});\n

    Here the helper renderHook abstracts away that oddity we had to do to get that hook tested. But rest assured it's doing essentially the same thing: creating a component under the hood that's running the hook lifecycle methods appropriately for you.

    \n

    Let's add a test to make sure it does the right thing with the API response and calls the right API

    \n
    import { renderHook, waitFor } from "@testing-library/react"; // add waitFor\n\ntest("to call the API and give back the pizza of the day", async () => {\n  fetch.mockResponseOnce(JSON.stringify(testPizza));\n  const { result } = renderHook(() => usePizzaOfTheDay(""));\n  await waitFor(() => {\n    expect(result.current).toEqual(testPizza);\n  });\n  expect(fetchMocker).toBeCalledWith("/api/pizza-of-the-day");\n});\n
      \n
    • waitFor is a handy trick where you need to wait for React to settle. You give it a body that throws errors until it's true. expect when it doesn't work throws an error so that's how this works.
    • \n
    • Once it resolves to true, it passes the test and moves on. If it fails like 20 times (that's configurable) it will then fail the test.
    • \n
    • Some people this is too into the implementation details and I half agree. However it is useful from the perspective that this hook needs to work in a certain way and it has an expectation of an API to call which does affect user behavior so it could be useful. If this was truly our codebase, I'd just test the PizzaOfTheDay component and call it good. But if we used this hook in lots of places, I'd probably a test just for it.
    • \n
    \n","markdown":"\nLet's say we needs tests for our custom hook, usePizzaOfTheDay. Testing custom hooks is a bit of a trick because they are inherently tied to the internal workings of React: they can't be called outside of a component. So how we do we get around that? We fake a component! Make a file called usePizzaOfTheDay.test.jsx in our `__tests__` directory.\n\n```javascript\nimport { expect, test, vi } from \"vitest\";\nimport { render } from \"@testing-library/react\";\nimport createFetchMock from \"vitest-fetch-mock\";\nimport { usePizzaOfTheDay } from \"../usePizzaOfTheDay\";\n\nconst fetchMocker = createFetchMock(vi);\nfetchMocker.enableMocks();\n\nconst testPizza = {\n id: \"calabrese\",\n name: \"The Calabrese Pizza\",\n category: \"Supreme\",\n description:\n \"Salami, Pancetta, Tomatoes, Red Onions, Friggitello Peppers, Garlic\",\n image: \"/public/pizzas/calabrese.webp\",\n sizes: { S: 12.25, M: 16.25, L: 20.25 },\n};\n\nfunction getPizzaOfTheDay() {\n let pizza;\n\n function TestComponent() {\n pizza = usePizzaOfTheDay();\n return null;\n }\n\n render();\n\n return pizza;\n}\n\ntest(\"gives null when first called\", async () => {\n fetch.mockResponseOnce(JSON.stringify(testPizza));\n const pizza = getPizzaOfTheDay();\n expect(pizza).toBeNull();\n});\n```\n\nIt's a little weird to implement a fake component to test something (we're dangerously close to the line of testing implementation details) but this is essentially library code and we want to assure ourselves this code works if we use it frequently in our code base.\n\nWe can make this better though. Let's rewrite our test to look like this:\n\n```javascript\nimport { renderHook } from \"@testing-library/react\"; // change import\n\ntest(\"to be null on initial load\", async () => {\n fetch.mockResponseOnce(JSON.stringify(testPizza));\n const { result } = renderHook(() => usePizzaOfTheDay(\"\"));\n expect(result.current).toBeNull();\n});\n```\n\nHere the helper `renderHook` abstracts away that oddity we had to do to get that hook tested. But rest assured it's doing essentially the same thing: creating a component under the hood that's running the hook lifecycle methods appropriately for you.\n\nLet's add a test to make sure it does the right thing with the API response and calls the right API\n\n```javascript\nimport { renderHook, waitFor } from \"@testing-library/react\"; // add waitFor\n\ntest(\"to call the API and give back the pizza of the day\", async () => {\n fetch.mockResponseOnce(JSON.stringify(testPizza));\n const { result } = renderHook(() => usePizzaOfTheDay(\"\"));\n await waitFor(() => {\n expect(result.current).toEqual(testPizza);\n });\n expect(fetchMocker).toBeCalledWith(\"/api/pizza-of-the-day\");\n});\n```\n\n- waitFor is a handy trick where you need to wait for React to settle. You give it a body that throws errors until it's true. `expect` when it doesn't work throws an error so that's how this works.\n- Once it resolves to true, it passes the test and moves on. If it fails like 20 times (that's configurable) it will then fail the test.\n- Some people this is too into the implementation details and I half agree. However it is useful from the perspective that this hook needs to work in a certain way and it has an expectation of an API to call which does affect user behavior so it could be useful. If this was truly our codebase, I'd just test the PizzaOfTheDay component and call it good. But if we used this hook in lots of places, I'd probably a test just for it.\n","slug":"testing-custom-hooks","title":"Testing Custom Hooks","section":"Testing","icon":"vial","filePath":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/07-testing/D-testing-custom-hooks.md","nextSlug":"/lessons/testing/snapshot-testing","prevSlug":"/lessons/testing/testing-user-interaction"}},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/testing/testing-user-interaction.json b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/testing/testing-user-interaction.json new file mode 100644 index 0000000..c22e6aa --- /dev/null +++ b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/testing/testing-user-interaction.json @@ -0,0 +1 @@ +{"pageProps":{"post":{"attributes":{"description":"Learn how to test a Contact page in React using Vitest and vitest-fetch-mock, a tool to simplify fetch mocking. The tutorial covers setting up mocks, rendering components with React Query's QueryClientProvider, filling out form inputs, simulating user interactions, and verifying API requests, providing a comprehensive guide for beginner to job-ready React developers under guidance from Brian Holt, creator of the Complete Intro to React course, version 9.","keywords":["React testing","Vitest","vitest-fetch-mock","React Query","Contact form testing"]},"html":"

    Let's test the Contact page. It has a nice, simple user interaction. User puts their information in, we submit to the API, and we show them a submitted header. Let's test all of that.

    \n

    First we need a helper function, vitest-fetch-mock. We don't need it because Vitest can do it, but I think it just makes things easier.

    \n
    npm i -D vitest-fetch-mock@0.3.0\n

    And now let's write the code

    \n
    import { render } from "@testing-library/react";\nimport { expect, test, vi } from "vitest";\nimport createFetchMock from "vitest-fetch-mock";\nimport { QueryClientProvider, QueryClient } from "@tanstack/react-query";\nimport { Route } from "../routes/contact.lazy";\n\nconst queryClient = new QueryClient();\n\nconst fetchMocker = createFetchMock(vi);\nfetchMocker.enableMocks();\n\ntest("can submit contact form", async () => {\n  fetchMocker.mockResponse(JSON.stringify({ status: "ok" }));\n  const screen = render(\n    <QueryClientProvider client={queryClient}>\n      <Route.options.component />\n    </QueryClientProvider>\n  );\n\n  const nameInput = screen.getByPlaceholderText("Name");\n  const emailInput = screen.getByPlaceholderText("Email");\n  const msgTextArea = screen.getByPlaceholderText("Message");\n\n  const testData = {\n    name: "Brian",\n    email: "test@example.com",\n    message: "This is a test message",\n  };\n\n  nameInput.value = testData.name;\n  emailInput.value = testData.email;\n  msgTextArea.value = testData.message;\n\n  const btn = screen.getByRole("button");\n\n  btn.click();\n\n  const h3 = await screen.findByRole("heading", { level: 3 });\n\n  expect(h3.innerText).toContain("Submitted");\n\n  const requests = fetchMocker.requests();\n  expect(requests.length).toBe(1);\n  expect(requests[0].url).toBe("/api/contact");\n  expect(fetchMocker).toHaveBeenCalledWith("/api/contact", {\n    body: JSON.stringify(testData),\n    headers: {\n      "Content-Type": "application/json",\n    },\n    method: "POST",\n  });\n});\n
      \n
    • We need to wrap React Query with a query provider always or it doesn't work. In this case we can just make that part of the initial render.
    • \n
    • You can pre-populate React Query with a valid cache and then test if your app uses that correctly. Totally valid way to test. In this case we want to make sure our endpoint is being posted to correctly (to make sure we record a contact)so we aren't doing that.
    • \n
    • vi is Vitest's spy library, similar to sinon.
    • \n
    • vitest-fetch-mock is just a nice layer on top of vi. We could just use vi directly.
    • \n
    • Notice we can just call click and it does all the things we'd expect. Cool, right?
    • \n
    • We then make some assertions that our h3 shows up correctly, that our API was called once, and that it was submitted to our API the way we expect it to. This may seem a bit implementation-oriented, and it is, but typically in a larger app we'd have a library for calling our API and we'd test that individually but we don't here so we're wrapping it all together.
    • \n
    \n","markdown":"Let's test the Contact page. It has a nice, simple user interaction. User puts their information in, we submit to the API, and we show them a submitted header. Let's test all of that.\n\nFirst we need a helper function, vitest-fetch-mock. We don't _need_ it because Vitest can do it, but I think it just makes things easier.\n\n```bash\nnpm i -D vitest-fetch-mock@0.3.0\n```\n\nAnd now let's write the code\n\n```javascript\nimport { render } from \"@testing-library/react\";\nimport { expect, test, vi } from \"vitest\";\nimport createFetchMock from \"vitest-fetch-mock\";\nimport { QueryClientProvider, QueryClient } from \"@tanstack/react-query\";\nimport { Route } from \"../routes/contact.lazy\";\n\nconst queryClient = new QueryClient();\n\nconst fetchMocker = createFetchMock(vi);\nfetchMocker.enableMocks();\n\ntest(\"can submit contact form\", async () => {\n fetchMocker.mockResponse(JSON.stringify({ status: \"ok\" }));\n const screen = render(\n \n \n \n );\n\n const nameInput = screen.getByPlaceholderText(\"Name\");\n const emailInput = screen.getByPlaceholderText(\"Email\");\n const msgTextArea = screen.getByPlaceholderText(\"Message\");\n\n const testData = {\n name: \"Brian\",\n email: \"test@example.com\",\n message: \"This is a test message\",\n };\n\n nameInput.value = testData.name;\n emailInput.value = testData.email;\n msgTextArea.value = testData.message;\n\n const btn = screen.getByRole(\"button\");\n\n btn.click();\n\n const h3 = await screen.findByRole(\"heading\", { level: 3 });\n\n expect(h3.innerText).toContain(\"Submitted\");\n\n const requests = fetchMocker.requests();\n expect(requests.length).toBe(1);\n expect(requests[0].url).toBe(\"/api/contact\");\n expect(fetchMocker).toHaveBeenCalledWith(\"/api/contact\", {\n body: JSON.stringify(testData),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n });\n});\n```\n\n- We need to wrap React Query with a query provider always or it doesn't work. In this case we can just make that part of the initial render.\n- You can pre-populate React Query with a valid cache and then test if your app uses that correctly. Totally valid way to test. In this case we want to make sure our endpoint is being posted to correctly (to make sure we record a contact)so we aren't doing that.\n- `vi` is Vitest's spy library, similar to sinon.\n- vitest-fetch-mock is just a nice layer on top of vi. We could just use vi directly.\n- Notice we can just call `click` and it does all the things we'd expect. Cool, right?\n- We then make some assertions that our h3 shows up correctly, that our API was called once, and that it was submitted to our API the way we expect it to. This may seem a bit implementation-oriented, and it is, but typically in a larger app we'd have a library for calling our API and we'd test that individually but we don't here so we're wrapping it all together.\n","slug":"testing-user-interaction","title":"Testing User Interaction","section":"Testing","icon":"vial","filePath":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/07-testing/C-testing-user-interaction.md","nextSlug":"/lessons/testing/testing-custom-hooks","prevSlug":"/lessons/testing/basic-react-tests"}},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/testing/vitest-ui.json b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/testing/vitest-ui.json new file mode 100644 index 0000000..20e118d --- /dev/null +++ b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/testing/vitest-ui.json @@ -0,0 +1 @@ +{"pageProps":{"post":{"attributes":{"title":"Vitest UI","description":"Learn how to use Vitest UI, an efficient tool for managing tests in your browser, by adding it to your project with simple npm commands. Enhance your React development skills by integrating browser-based testing with Vitest for convenient module graph visualization and individual test management. Ideal for developers seeking practical tools within the Complete Intro to React course by Brian Holt.","keywords":["Vitest UI","React testing","Brian Holt","npm","module graph","React development"]},"html":"

    Another fun little tool let's you use is Vitest UI. It allows you to see and manage all your tests from the web browser. Let's add it.

    \n
    npm i -D @vitest/ui@2.1.3\n

    And in your package.json

    \n
    // in scripts\n"test:ui": "vitest --ui"\n

    Now from the command line run npm run test:ui and it should pop up your browser with the Vitest UI open. Super convenient, particularly in places you need to run individual tests repeatedly and you don't have the VS Code extension. The module graph is a cool visualization as well.

    \n","markdown":"\nAnother fun little tool let's you use is [Vitest UI][ui]. It allows you to see and manage all your tests from the web browser. Let's add it.\n\n```bash\nnpm i -D @vitest/ui@2.1.3\n```\n\nAnd in your package.json\n\n```json\n// in scripts\n\"test:ui\": \"vitest --ui\"\n```\n\nNow from the command line run `npm run test:ui` and it should pop up your browser with the Vitest UI open. Super convenient, particularly in places you need to run individual tests repeatedly and you don't have the VS Code extension. The module graph is a cool visualization as well.\n\n[ui]: https://vitest.dev/guide/ui.html\n","slug":"vitest-ui","title":"Vitest UI","section":"Testing","icon":"vial","filePath":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/07-testing/G-vitest-ui.md","nextSlug":"/lessons/testing/browser-tests","prevSlug":"/lessons/testing/coverage"}},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/testing/vitest.json b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/testing/vitest.json new file mode 100644 index 0000000..d262420 --- /dev/null +++ b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/testing/vitest.json @@ -0,0 +1 @@ +{"pageProps":{"post":{"attributes":{"description":"Learn how to set up and perform testing on React applications using Vitest, a test runner compatible with Vite's build pipeline, from Brian Holt's Complete Intro to React course. This guide provides step-by-step instructions for integrating Vitest with @testing-library/react for efficient React testing, drawing on similarities with Jasmine and Jest APIs to deliver seamless testing experiences. Ideal for React developers seeking efficient test integration, this tutorial emphasizes real-time feedback and browser-like test environments with happy-dom.","keywords":["React testing","Vitest","Jest","React development","Brian Holt"]},"html":"

    This is meant to be a very brief treatise on how to do testing on React applications. This will be a brief intro on how to set up Vitest tests for the application we just created. For more information about Vite, Vitest, and testing in general, check out the Build Tools & Testing Learning Path.

    \n

    Testing with Vitest

    \n

    Vitest is a test runner made by the fine folks who make Vite (as well as Vue.) The idea behind Vitest is that you already have a complete build pipeline for making an app, why should that pipeline be any different for test? It shouldn't; you want your testing environment to look as much like your app environment as possible.

    \n

    They designed it to be a drop-in replacement for Jest which is what I have taught for this course since the beginning. Jest is great and still a very viable tool to use for testing, even with Vite. We're just going to use Vitest because 1. we don't have to do any more configuration and 2. 100% of what you will learn in here is going to be useful if you use Jest. Win-win. If you want to learn Jest specifically, take a look at Intermediate React v4's testing section.

    \n
    \n

    Fun side note: Jest is now an OpenJS project and no longer directly under Facebook.

    \n
    \n

    While Vitest is not using Jasmine directly, its APIs mimic Jasmine APIs (just like Jest.)

    \n

    Let's get going. Run npm install -D vitest@2.1.3 @testing-library/react@16.0.1 happy-dom@15.7.4.

    \n

    @testing-library/react, formerly called react-testing-library, is a tool that has a bunch of convenience features that make testing React significantly easier and is now the recommended way of testing React, supplanting Enzyme. Previous versions of this course teach Enzyme if you'd like to see that (though I wouldn't recommend it unless you have to.)

    \n

    We need to tell Vitest that we need a browser-like environment which it will fulfill via the happy-dom package. happy-dom is a lot like jsdom but smaller, doesn't do 100% of what the browser does, and is much, much faster.

    \n

    Next go into your src directory and create a folder called __tests__. Notice that's double underscores on both sides. Why double? They borrowed it from Python where double underscores ("dunders" as I've heard them called) mean something magic happens (in essence it means the name itself has significance and something is looking for that path name exactly.) In this case, Vitest assumes all JS files in here are tests.

    \n

    Let's go add an npm script. In your package.json.

    \n
    "test": "vitest"\n
    \n

    Fun trick: if you call it test, npm lets you run that command as just npm t.

    \n
    \n

    This command lets you run Jest in an interactive mode where it will re-run tests selectively as you save them. This lets you get instant feedback if your test is working or not. This is probably my favorite feature of Vitest.

    \n

    Okay, one little configuration to add to your vite.config.js.

    \n
    // add this to the config object\ntest: {\n  environment: "happy-dom",\n},\n

    Now that we've got that going, let's go write a test.

    \n","markdown":"\nThis is meant to be a very brief treatise on how to do testing on React applications. This will be a brief intro on how to set up Vitest tests for the application we just created. For more information about Vite, Vitest, and testing in general, check out the [Build Tools & Testing Learning Path][lp].\n\n## Testing with Vitest\n\n[Vitest][vitest] is a test runner made by the fine folks who make Vite (as well as Vue.) The idea behind Vitest is that you already have a complete build pipeline for making an app, why should that pipeline be any different for test? It shouldn't; you want your testing environment to look as much like your app environment as possible.\n\nThey designed it to be a drop-in replacement for [Jest][jest] which is what I have taught for this course since the beginning. Jest is great and still a very viable tool to use for testing, even with Vite. We're just going to use Vitest because 1. we don't have to do any more configuration and 2. 100% of what you will learn in here is going to be useful if you use Jest. Win-win. If you want to learn Jest specifically, [take a look at Intermediate React v4's testing section.][v4]\n\n> Fun side note: [Jest is now an OpenJS project and no longer directly under Facebook][fb].\n\nWhile Vitest is not using Jasmine directly, its APIs mimic Jasmine APIs (just like Jest.)\n\nLet's get going. Run `npm install -D vitest@2.1.3 @testing-library/react@16.0.1 happy-dom@15.7.4`.\n\n`@testing-library/react`, formerly called `react-testing-library`, is a tool that has a bunch of convenience features that make testing React significantly easier and is now the recommended way of testing React, supplanting [Enzyme][enzyme]. Previous versions of this course teach Enzyme if you'd like to see that (though I wouldn't recommend it unless you have to.)\n\nWe need to tell Vitest that we need a browser-like environment which it will fulfill via the [happy-dom][hd] package. happy-dom is a lot like jsdom but smaller, doesn't do 100% of what the browser does, and is much, much faster.\n\nNext go into your src directory and create a folder called `__tests__`. Notice that's double underscores on both sides. Why double? They borrowed it from Python where double underscores (\"dunders\" as I've heard them called) mean something magic happens (in essence it means the name itself has significance and something is looking for that path name exactly.) In this case, Vitest assumes all JS files in here are tests.\n\nLet's go add an npm script. In your package.json.\n\n```json\n\"test\": \"vitest\"\n```\n\n> Fun trick: if you call it test, npm lets you run that command as just `npm t`.\n\nThis command lets you run Jest in an interactive mode where it will re-run tests selectively as you save them. This lets you get instant feedback if your test is working or not. This is probably my favorite feature of Vitest.\n\nOkay, one little configuration to add to your vite.config.js.\n\n```javascript\n// add this to the config object\ntest: {\n environment: \"happy-dom\",\n},\n```\n\nNow that we've got that going, let's go write a test.\n\n[jest]: https://jestjs.io\n[jasmine]: https://jasmine.github.io/\n[enzyme]: http://airbnb.io/enzyme/\n[istanbul]: https://istanbul.js.org\n[res]: https://raw.githubusercontent.com/btholt/complete-intro-to-react-v5/testing/__mocks__/@frontendmasters/res.json\n[app]: https://github.com/btholt/citr-v8-project/tree/master/14-context\n[fb]: https://twitter.com/cpojer/status/1524419433938046977\n[hd]: https://github.com/capricorn86/happy-dom\n[vitest]: https://vitest.dev/\n[v4]: https://frontendmasters.com/courses/intermediate-react-v4/setup-jest-testing-library/\n[lp]: https://frontendmasters.com/learn/build-tools/","slug":"vitest","title":"Vitest","section":"Testing","icon":"vial","filePath":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/07-testing/A-vitest.md","nextSlug":"/lessons/testing/basic-react-tests","prevSlug":"/lessons/advanced-react/uncontrolled-forms"}},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/tools/code-formatting.json b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/tools/code-formatting.json new file mode 100644 index 0000000..38dcb04 --- /dev/null +++ b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/tools/code-formatting.json @@ -0,0 +1 @@ +{"pageProps":{"post":{"attributes":{"description":"Learn how to maintain high code quality with Prettier by James Long, a powerful tool for consistent code formatting. Discover easy integration methods and how to use Prettier with Visual Studio Code to streamline your development process, as well as setup instructions for npm scripts in this guide from Brian Holt's Complete Intro to React course.","keywords":["Prettier","code quality","ESLint","Brian Holt","React development","Visual Studio Code","npm scripts"]},"html":"

    Prettier

    \n

    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.

    \n

    Prettier is an amazing tool from the brain of James Long. 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.

    \n

    Prettier is a really fancy pretty printer. It takes the code you write, breaks it down in to an abstract syntax tree (AST) which is just a representation of your code. It then takes that AST, throws away all of your code style you made and prints it back out using a predefined style. While this sounds a little scary, it's actually really cool. Since you no longer have control of the style of your code, you no longer have to think about it at all. Your code is always consistent, as is the code from the rest of your team. No more bikeshedding!! As I like to put it: if your brain is a processor, you get to free up the thread of your brain that worries about code styles and readability: it just happens for you. Don't like semicolons? Don't write them! It puts them in for you. I love Prettier.

    \n

    Need to tool around a bit with it before you trust it? Go here. You can see how it works.

    \n

    Let's go integrate this into our project. It's pretty easy (since I'm a dad now, I'm legally obligated to make this joke.)

    \n

    Either install Prettier globally npm install --global prettier or replace when I run prettier with (from the root of your project) npx prettier. From there, run prettier src/App.js. This will output the formatted version of your file. If you want to actually write the file, run prettier --write src/App.js. Go check src/App.js and see it has been reformatted a bit. I will say for non-JSX React, prettier makes your code less readable. Luckily Prettier supports JSX! We'll get to that shortly.

    \n

    Prettier has a few configurations but it's mostly meant to be a tool everyone uses and doesn't argue/bikeshed about the various code style rules. Here they are. I just use it as is since I'm lazy. Prettier can also understand flow and TypeScript.

    \n

    Prettier is great to use with Visual Studio Code. Just download this extension. Pro tip: set it to only run Prettier when it detects a Prettier config file. Makes it so you never have to turn it off. In order to do that, set prettier.requireConfig to true and editor.formatOnSave to true.

    \n

    So that our tool can know this is a Prettier project, we're going to create a file called .prettierrc and put {} in it. This lets everyone know this is a Prettier project that uses the default configuration. You can put other configs here if you hold strong formatting opinions.

    \n

    npm scripts

    \n

    So it can be painful to try to remember the various CLI commands to run on your project. You can put CLI commands into it and then run the name of the tag and it'll run that script. Let's go see how that works. Put the following into your package.json.

    \n

    First run npm install -D prettier@3.3.3 -D means it's for development only.

    \n
    "scripts": {\n    "format": "prettier --write \\"src/**/*.{js,jsx}\\""\n},\n

    Now you can run npm run format and it will run that command. This means we don't have to remember that mess of a command and just have to remember format. Nice, right? We'll be leaning on this a lot during this course.

    \n
    \n

    Note the @3.3.3 portion. For the purposes of making this course not break in the future, I have you install the exact version of packages I used when I made this course. As is natural, packages change and progress over time and I can't anticipate how that will happen. So I'd suggest you use the same packages I do as you do this course (even if npm yells at you for security vulnerabilites). As soon as you're done with the course, feel free to go update the versions to the latest and see if anything breaks.

    \n
    \n","markdown":"\n## Prettier\n\nIt'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.\n\n[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.\n\nPrettier is a really fancy pretty printer. It takes the code you write, breaks it down in to an abstract syntax tree (AST) which is just a representation of your code. It then takes that AST, throws away all of your code style you made and prints it back out using a predefined style. While this sounds a little scary, it's actually really cool. Since you no longer have control of the style of your code, you no longer have to think about it at all. Your code is always consistent, as is the code from the rest of your team. No more bikeshedding!! As I like to put it: if your brain is a processor, you get to free up the thread of your brain that worries about code styles and readability: it just happens for you. Don't like semicolons? Don't write them! It puts them in for you. I _love_ Prettier.\n\nNeed to tool around a bit with it before you trust it? [Go here][prettier-playground]. You can see how it works.\n\nLet's go integrate this into our project. It's _pretty_ easy (since I'm a dad now, I'm legally obligated to make this joke.)\n\nEither install Prettier globally `npm install --global prettier` or replace when I run `prettier` with (from the root of your project) `npx prettier`. From there, run `prettier src/App.js`. This will output the formatted version of your file. If you want to actually write the file, run `prettier --write src/App.js`. Go check src/App.js and see it has been reformatted a bit. I will say for non-JSX React, prettier makes your code less readable. Luckily Prettier supports JSX! We'll get to that shortly.\n\nPrettier has a few configurations but it's mostly meant to be a tool everyone uses and doesn't argue/bikeshed about the various code style rules. [Here they are][prettier-options]. I just use it as is since I'm lazy. Prettier can also understand [flow][flow] and [TypeScript][ts].\n\nPrettier is great to use with [Visual Studio Code][vscode]. Just download [this extension][vscode-prettier]. Pro tip: set it to only run Prettier when it detects a Prettier config file. Makes it so you never have to turn it off. In order to do that, set `prettier.requireConfig` to `true` and `editor.formatOnSave` to true.\n\nSo that our tool can know this is a Prettier project, we're going to create a file called `.prettierrc` and put `{}` in it. This lets everyone know this is a Prettier project that uses the default configuration. You can put other configs here if you hold strong formatting opinions.\n\n## npm scripts\n\nSo it can be painful to try to remember the various CLI commands to run on your project. You can put CLI commands into it and then run the name of the tag and it'll run that script. Let's go see how that works. Put the following into your package.json.\n\nFirst run `npm install -D prettier@3.3.3` `-D` means it's for development only.\n\n```json\n\"scripts\": {\n\t\"format\": \"prettier --write \\\"src/**/*.{js,jsx}\\\"\"\n},\n```\n\nNow you can run `npm run format` and it will run that command. This means we don't have to remember that mess of a command and just have to remember format. Nice, right? We'll be leaning on this a lot during this course.\n\n> Note the `@3.3.3` portion. For the purposes of making this course not break in the future, I have you install the _exact_ version of packages I used when I made this course. As is natural, packages change and progress over time and I can't anticipate how that will happen. So I'd suggest you use the same packages I do as you do this course (even if npm yells at you for security vulnerabilites). As soon as you're done with the course, feel free to go update the versions to the latest and see if anything breaks.\n\n[jlongster]: https://twitter.com/jlongster\n[prettier]: https://github.com/prettier/prettier\n[prettier-playground]: https://prettier.io/playground/\n[prettier-options]: https://prettier.io/docs/en/options.html\n[flow]: https://flow.org/\n[prettier-ide]: https://prettier.io/docs/en/editors\n[ts]: https://www.typescriptlang.org/\n[vscode]: https://code.visualstudio.com/?WT.mc_id=reactintro-github-brholt\n[vscode-prettier]: https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode&WT.mc_id=reactintro-github-brholt\n","slug":"code-formatting","title":"Code Formatting","section":"Tools","icon":"screwdriver-wrench","filePath":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/03-tools/B-code-formatting.md","nextSlug":"/lessons/tools/linting","prevSlug":"/lessons/tools/npm"}},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/tools/git.json b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/tools/git.json new file mode 100644 index 0000000..cb104b8 --- /dev/null +++ b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/tools/git.json @@ -0,0 +1 @@ +{"pageProps":{"post":{"attributes":{"description":"Learn how to efficiently set up a Git repository, create a .gitignore file, and manage untracked files in your project as part of the Complete Intro to React v9 course by Brian Holt. Enhance your Git skills with practical tips, ensuring a clean and professional project environment. For more detailed instruction, explore ThePrimeagen's Git course on Frontend Masters.","keywords":["Git setup","Git repository",".gitignore","Front End Masters","frontend development"]},"html":"

    Git

    \n

    Git is a critical part of any project and probably something many of you are already familiar with. Install 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.)

    \n

    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:

    \n
    node_modules\ndist/\n.env\n.DS_Store\ncoverage/\n.vscode/\n

    This will make it so these things won't get added to our repo. If you want more Git instruction, please check out the courses from ThePrimeagen and Nina Zakharenko on Frontend Masters.

    \n","markdown":"\n## Git\n\nGit 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.)\n\nIf 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:\n\n```\nnode_modules\ndist/\n.env\n.DS_Store\ncoverage/\n.vscode/\n```\n\nThis will make it so these things won't get added to our repo. If you want more Git instruction, please check out the courses from [ThePrimeagen][prime] and [Nina Zakharenko][nina] on Frontend Masters.\n\n[prime]: https://frontendmasters.com/courses/everything-git/\n[git]: https://git-scm.com/book/en/v2/Getting-Started-Installing-Git\n[nina]: https://frontendmasters.com/courses/git-in-depth/","slug":"git","title":"Git","section":"Tools","icon":"screwdriver-wrench","filePath":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/03-tools/D-git.md","nextSlug":"/lessons/tools/vite","prevSlug":"/lessons/tools/linting"}},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/tools/linting.json b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/tools/linting.json new file mode 100644 index 0000000..7740f01 --- /dev/null +++ b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/tools/linting.json @@ -0,0 +1 @@ +{"pageProps":{"post":{"attributes":{"title":"ESLint","description":"Learn how to set up and configure ESLint with Prettier for effective JavaScript code linting in your React projects, featuring tools like ESLint's JS config and globals package, under the guidance of seasoned developer Brian Holt.","keywords":["ESLint","Prettier","JavaScript","react","Brian Holt"]},"html":"

    ESLint

    \n

    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 comes into play here. It will lint for these problems.

    \n

    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.

    \n

    There are dozens of preset configs for ESLint and you're welcome to use any one of them. The Airbnb config 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.

    \n
    \n

    We're using .mjs (module JS) because we want to use import/export for modules instead of require.

    \n
    \n

    Add this to the eslint.config.mjs file:

    \n
    import js from "@eslint/js";\nimport globals from "globals";\nimport prettier from "eslint-config-prettier";\n\n/** @type {import('eslint').Linter.Config[]} */\nexport default [\n  js.configs.recommended,\n  {\n    files: ["**/*.js"],\n    languageOptions: {\n      globals: { ...globals.browser, ...globals.node },\n      parserOptions: {\n        ecmaFeatures: {\n          jsx: true,\n        },\n      },\n    },\n  },\n  prettier,\n];\n
      \n
    • 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.)
    • \n
    • 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.
    • \n
    • 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.
    • \n
    • 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.
    • \n
    \n

    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. Let's add ESLint to our scripts:

    \n
    "lint": "eslint",\n

    Run npm run eslint now and you should see we have a few errors.

    \n
    \n

    🚨 ESLint will have a bunch of errors right now. Ignore them; we'll fix them in a sec.

    \n
    \n

    Worth adding three things here:

    \n
      \n
    • With npm scripts, you can pass additional parameters to the command if you want. Just add a -- and then put whatever else you want to tack on after that. For example, if I wanted to get the debug output from ESLint, I could run npm run lint -- --debug which would translate to eslint --debug.
    • \n
    • We can use our fix trick this way: npm run lint -- --fix.
    • \n
    • We're going to use both JS and JSX.
    • \n
    \n

    ESLint is a cinch to get working with Visual Studio Code. Just download the extension.

    \n

    oxlint and Biome

    \n

    Two projects to watch going forward here: Biome (formerly called Rome) and oxlint. Both are written in Rust and designed to be faster than ESLint (which is written in JavaScript). ESLint at huge scale can be a bit slow and these two projects aim to fix that bottleneck. I'd still say it's early days on these projects and 99% of the time ESLint is fast enough. Still, good to keep an eye on both of the projects. Eventually both projects aim to replace Prettier as well.

    \n","markdown":"\n## ESLint\n\nOn 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 these problems.\n\nFirst 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.\n\nThere 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.\n\n> We're using .mjs (module JS) because we want to use import/export for modules instead of require.\n\nAdd this to the `eslint.config.mjs` file:\n\n```js\nimport js from \"@eslint/js\";\nimport globals from \"globals\";\nimport prettier from \"eslint-config-prettier\";\n\n/** @type {import('eslint').Linter.Config[]} */\nexport default [\n js.configs.recommended,\n {\n files: [\"**/*.js\"],\n languageOptions: {\n globals: { ...globals.browser, ...globals.node },\n parserOptions: {\n ecmaFeatures: {\n jsx: true,\n },\n },\n },\n },\n prettier,\n];\n```\n\n- 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.)\n- 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.\n- [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.\n- 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.\n\nThis is a combination of the recommended configs of ESLint and Prettier. This will lint for both normal JS stuff as well as JSX stuff. Let's add ESLint to our scripts:\n\n```json\n\"lint\": \"eslint\",\n```\n\nRun `npm run eslint` now and you should see we have a few errors.\n\n> 🚨 ESLint will have a bunch of errors right now. Ignore them; we'll fix them in a sec.\n\nWorth adding three things here:\n\n- With npm scripts, you can pass additional parameters to the command if you want. Just add a `--` and then put whatever else you want to tack on after that. For example, if I wanted to get the debug output from ESLint, I could run `npm run lint -- --debug` which would translate to `eslint --debug`.\n- We can use our fix trick this way: `npm run lint -- --fix`.\n- We're going to use both JS and JSX.\n\nESLint is a cinch to get working with [Visual Studio Code][vscode]. Just download [the extension][vscode-eslint].\n\n## oxlint and Biome\n\nTwo projects to watch going forward here: [Biome][biome] (formerly called Rome) and [oxlint][oxlint]. Both are written in Rust and designed to be faster than ESLint (which is written in JavaScript). ESLint at huge scale can be a bit slow and these two projects aim to fix that bottleneck. I'd still say it's early days on these projects and 99% of the time ESLint is fast enough. Still, good to keep an eye on both of the projects. Eventually both projects aim to replace Prettier as well.\n\n[eslint]: https://eslint.org\n[vscode-eslint]: https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint\n[airbnb]: https://github.com/airbnb/javascript\n[vscode]: https://code.visualstudio.com/\n[biome]: https://biomejs.dev/\n[oxlint]: https://oxc.rs/docs/guide/usage/linter.html\n[globals]: https://www.npmjs.com/package/globals\n","slug":"linting","title":"ESLint","section":"Tools","icon":"screwdriver-wrench","filePath":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/03-tools/C-linting.md","nextSlug":"/lessons/tools/git","prevSlug":"/lessons/tools/code-formatting"}},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/tools/npm.json b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/tools/npm.json new file mode 100644 index 0000000..38a13bc --- /dev/null +++ b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/tools/npm.json @@ -0,0 +1 @@ +{"pageProps":{"post":{"attributes":{"title":"npm","description":"Learn about npm and pnpm, two essential package managers for Node.js, as you embark on your React development journey with Brian Holt's Complete Intro to React, v9. This section guides you through setting up npm projects and understanding the differences between npm and pnpm, enhancing your knowledge of JavaScript development tools.","keywords":["npm","pnpm","Node.js","React development","JavaScript tools"]},"html":"

    npm

    \n

    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.

    \n

    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.

    \n

    pnpm

    \n

    Another option here is to use 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.

    \n","markdown":"\n## npm\n\nnpm 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.\n\nIn 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.\n\n## pnpm\n\nAnother 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.\n\n[pnpm]: https://pnpm.io/motivation\n[node]: https://nodejs.org/en\n","slug":"npm","title":"npm","section":"Tools","icon":"screwdriver-wrench","filePath":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/03-tools/A-npm.md","nextSlug":"/lessons/tools/code-formatting","prevSlug":"/lessons/no-frills-react/components"}},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/tools/vite.json b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/tools/vite.json new file mode 100644 index 0000000..5781aff --- /dev/null +++ b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/tools/vite.json @@ -0,0 +1 @@ +{"pageProps":{"post":{"attributes":{"description":"Learn to set up your React project using Vite, a fast build tool preferred by the React community, with step-by-step guidance from Brian Holt. This tutorial covers the installation and configuration of Vite, React, and essential plugins to optimize your development environment.","keywords":["React","Vite","Brian Holt","JavaScript","frontend development"]},"html":"

    Vite

    \n

    The build tool we are going to be using today is called Vite. Vite (pronounced "veet", meaning quick in French) is a tool put out by the Vue team that ultimately ends up wrapping Rollup which does the actual bundling. The end result is a tool that is both easy to use and produces a great end result.

    \n
    \n

    Fun fact: there's a new project being developed called Rolldown which is written in Rust and aims to replace Rollup.

    \n
    \n

    Our end result that we want from a build tool is that

    \n
      \n
    • We can separate files out for code organization and have a tool stitch them together for us
    • \n
    • We can include external, third-party libraries from npm (like React!)
    • \n
    • The tool will optimize the code for us by minifying and other optimizing techniques
    • \n
    \n

    Previous versions of this course used Parcel, another tool near-and-dear to my heart. It is still an amazing tool and one I recommend you check out. We ended up moving to Vite because the React community has selected it as the tool-of-choice for the moment and this course aims to give you the community norms of React. Even older versions of this course previously taught Webpack.

    \n

    First, let's install the things we need for Vite.

    \n
    npm install -D vite@5.4.2 @vitejs/plugin-react@4.3.1\n

    The former is the tool itself and the latter is all the React specific features we will need. Now that we have those installed, we need to modify our index.html just a little bit.

    \n
    <!-- delete the two unpkg script lines -->\n<script type="module" src="./src/App.js"></script>\n

    We need to add module to the script tag so that the browser knows it's working with modern browser technology that allows you in development mode to use modules directly. Instead of having to reload the whole bundle every time, your browser can just reload the JS that has changed. It allows the browser to crawl the dependency graph itself which means Vite can run lightning fast in dev mode. It will still package it up for production so we can support a range of browsers.

    \n

    Next, let's make our config file. Make a file in the root of your project called vite.config.js and stick this in there:

    \n
    import { defineConfig } from "vite";\nimport react from "@vitejs/plugin-react";\n\nexport default defineConfig({\n  plugins: [react()],\n});\n

    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.

    \n

    Okay, let's actually install React to our project.

    \n
    npm install react@18.3.1 react-dom@18.3.1\n
      \n
    • We did not include the -D because React is not a development tool, it's a production dependency
    • \n
    • React and ReactDOM are versioned together so you can assume those versions will always be the same
    • \n
    \n

    Finally, head to App.js and modify the following

    \n
    // add to the top\nimport React from "react";\nimport { createRoot } from "react-dom/client";\n\n// modify the createRoot call, delete "ReactDOM"\nconst root = createRoot(container);\n

    Now let's set up our scripts to start Vite. In package.json, put:

    \n
    // inside scripts\n"dev": "vite",\n"build": "vite build",\n"preview": "vite preview"\n

    Be sure to also add "type: module" to your package.json. Vite has deprecated support for Common.js and now requires you to use ESM style modules.

    \n
    \n

    Note: you will get a warning from Vite like Files in the public directory are served at the root path. Instead of /public/style.css, use /style.css. – ignore this, we'll fix it in a bit.

    \n
    \n

    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.

    \n
    \n

    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.

    \n
    \n

    Alternatives

    \n

    There are a myriad of fantastic developer tools out there available. We chose Vite because the industry has been using it for a while but I have zero problem with you selecting other tools. Just trying to expose everyone to great tools.

    \n

    In particular, esbuild is a wonderful tool to take a look at as well.

    \n
    \n

    🏁 Click here to see the state of the project up until now: 02-tools.

    \n
    \n","markdown":"\n## Vite\n\nThe 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.\n\n> Fun fact: there's a new project being developed called [Rolldown][rolldown] which is written in Rust and aims to replace Rollup.\n\nOur end result that we want from a build tool is that\n\n- We can separate files out for code organization and have a tool stitch them together for us\n- We can include external, third-party libraries from npm (like React!)\n- The tool will optimize the code for us by minifying and other optimizing techniques\n\nPrevious versions of this course used [Parcel][parcel], another tool near-and-dear to my heart. It is still an amazing tool and one I recommend you check out. We ended up moving to Vite because the React community has selected it as the tool-of-choice for the moment and this course aims to give you the community norms of React. Even older versions of this course previously taught [Webpack][webpack].\n\nFirst, let's install the things we need for Vite.\n\n```bash\nnpm install -D vite@5.4.2 @vitejs/plugin-react@4.3.1\n```\n\nThe former is the tool itself and the latter is all the React specific features we will need. Now that we have those installed, we need to modify our index.html just a little bit.\n\n```html\n\n\n```\n\nWe need to add module to the script tag so that the browser knows it's working with modern browser technology that allows you in development mode to use modules directly. Instead of having to reload the whole bundle every time, your browser can just reload the JS that has changed. It allows the browser to crawl the dependency graph itself which means Vite can run lightning fast in dev mode. It will still package it up for production so we can support a range of browsers.\n\nNext, let's make our config file. Make a file in the root of your project called `vite.config.js` and stick this in there:\n\n```javascript\nimport { defineConfig } from \"vite\";\nimport react from \"@vitejs/plugin-react\";\n\nexport default defineConfig({\n plugins: [react()],\n});\n```\n\nBy 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.\n\nOkay, let's _actually_ install React to our project.\n\n```bash\nnpm install react@18.3.1 react-dom@18.3.1\n```\n\n- We did not include the `-D` because React is not a development tool, it's a production dependency\n- React and ReactDOM are versioned together so you can assume those versions will always be the same\n\nFinally, head to App.js and modify the following\n\n```javascript\n// add to the top\nimport React from \"react\";\nimport { createRoot } from \"react-dom/client\";\n\n// modify the createRoot call, delete \"ReactDOM\"\nconst root = createRoot(container);\n```\n\nNow let's set up our scripts to start Vite. In package.json, put:\n\n```json\n// inside scripts\n\"dev\": \"vite\",\n\"build\": \"vite build\",\n\"preview\": \"vite preview\"\n```\n\nBe sure to also add `\"type: module\"` to your package.json. Vite has deprecated support for Common.js and now requires you to use ESM style modules.\n\n> Note: you will get a warning from Vite like `Files in the public directory are served at the root path.\nInstead of /public/style.css, use /style.css.` – ignore this, we'll fix it in a bit.\n\n`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.\n\n> 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.\n\n## Alternatives\n\nThere are a myriad of fantastic developer tools out there available. We chose Vite because the industry has been using it for a while but I have zero problem with you selecting other tools. Just trying to expose everyone to great tools.\n\nIn particular, [esbuild][esbuild] is a wonderful tool to take a look at as well.\n\n> 🏁 [Click here to see the state of the project up until now: 02-tools][step].\n\n[step]: https://github.com/btholt/citr-v9-project/tree/master/02-tools\n[webpack]: https://webpack.js.org/\n[parcel]: https://parceljs.org/\n[rollup]: https://www.rollupjs.org/\n[vite]: https://vitejs.dev/\n[rolldown]: https://rolldown.rs/\n[esbuild]: https://esbuild.github.io/\n","slug":"vite","title":"Vite","section":"Tools","icon":"screwdriver-wrench","filePath":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/03-tools/E-vite.md","nextSlug":"/lessons/core-react-concepts/jsx","prevSlug":"/lessons/tools/git"}},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/welcome/intro.json b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/welcome/intro.json new file mode 100644 index 0000000..cc100a8 --- /dev/null +++ b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/welcome/intro.json @@ -0,0 +1 @@ +{"pageProps":{"post":{"attributes":{"description":"Join Brian Holt in \"Complete Intro to React, v9,\" a beginner-friendly course that equips you with essential React skills using a first-principles approach, incorporating tools like Vite and JSX, making you job-ready in the React ecosystem.","keywords":["Brian Holt","React","web development","javascript","frontend","Vite","JSX"]},"html":"
    \n

    🚨 You do not need to watch/read previous versions of this course. This is just the ninth revision of the course.

    \n
    \n

    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.

    \n

    Who is this course for?

    \n

    You! This course is for anyone looking to get started in React or deepen their expertise in it. No experience is necessary. However, I guarantee anyone who is interested in React will learn something in here. This probably shouldn't be your first programming or JavaScript course, but beyond that, it should be comfortable for anyone. We do use some Node.js to run our little API and for the build tools but no real Node.js experience is necessary, I'll give you the commands necessary to run it.

    \n

    Who am I?

    \n

    \"Brian

    \n

    My name is Brian Holt and I've been writing React for a long time. I shipped Reddit's first React code in 2014.

    \n

    We shipped reddit's first production @reactjs code last week, our checkout process.https://t.co/KUInwsCmAF

    — Brian Holt (@holtbt) July 28, 2014
    \n\n

    I went on to write React at a variety of large companies and it's been my goto tool for over a decade at this point. It's safe to say it's been a defining feature in my career.

    \n

    I currently work as a staff product manager at Neon working on developer tools and developer experience. I loved working on dev tools and dev experiences so much that I ended up working in tools and strategy. Previous to Neon, I've worked at Snowflake, Microsoft, LinkedIn, Netflix, Reddit, and some other startups. I've done everything from VP of product to dev rel to staff engineering tech lead.

    \n

    When I'm not working or teaching, you'll find me hanging out with my wife, son, and my soon-to-arrive daughter! I've lived the past six years in Seattle but I'm currently moving to Sacramento to get a bit more sun and to live close to amazing snowboarding in Tahoe! 🏂 I also enjoy hazy IPAs, Islay Scotches, road cycling, and playing Dota 2 poorly.

    \n

    Please catch up with me on social media! Be aware that I'm awful at responding to DMs!!

    \n\n

    Where to File Issues

    \n

    I write these courses and take care to not make mistakes. However when teaching over ten hours of material, mistakes are inevitable, both here in the grammar and in the course with the material. However I (and the wonderful team at Frontend Masters) are constantly correcting the mistakes so that those of you that come later get the best product possible. If you find a mistake we'd love to fix it. The best way to do this is to open a pull request or file an issue on the GitHub repo. While I'm always happy to chat and give advice on social media, I can't be tech support for everyone. And if you file it on GitHub, those who come later can Google the same answer you got.

    \n

    How the repo works

    \n

    There are two repos for this class: the website you're currently on and the example projects. To get set up, clone or download the projects repo:

    \n
    git clone https://github.com/btholt/citr-v9-project.git\n

    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!

    \n

    The naming format will be XX-<name of the lesson> 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.

    \n

    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.

    \n
    \n

    And one last request! Please star this repo. It helps the course be more discoverable and with my fragile ego.

    \n
    \n","markdown":"\n> 🚨 You do not need to watch/read previous versions of this course. This is just the ninth revision of the course.\n\nHello! 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.\n\n## Who is this course for?\n\nYou! This course is for anyone looking to get started in React or deepen their expertise in it. No experience is necessary. However, I guarantee anyone who is interested in React will learn something in here. This probably shouldn't be your first programming or JavaScript course, but beyond that, it should be comfortable for anyone. We do use some Node.js to run our little API and for the build tools but no real Node.js experience is necessary, I'll give you the commands necessary to run it.\n\n## Who am I?\n\n![Brian teaching](/images/social-share-cover.jpg)\n\nMy name is Brian Holt and I've been writing React for a long time. I shipped Reddit's first React code in 2014.\n\n

    We shipped reddit's first production @reactjs code last week, our checkout process.https://t.co/KUInwsCmAF

    — Brian Holt (@holtbt) July 28, 2014
    \n\nI went on to write React at a variety of large companies and it's been my goto tool for over a decade at this point. It's safe to say it's been a defining feature in my career.\n\nI currently work as a staff product manager at [Neon][neon] working on developer tools and developer experience. I loved working on dev tools and dev experiences so much that I ended up working in tools and strategy. Previous to Neon, I've worked at Snowflake, Microsoft, LinkedIn, Netflix, Reddit, and some other startups. I've done everything from VP of product to dev rel to staff engineering tech lead.\n\nWhen I'm not working or teaching, you'll find me hanging out with my wife, son, and my soon-to-arrive daughter! I've lived the past six years in Seattle but I'm currently moving to Sacramento to get a bit more sun and to live close to amazing snowboarding in Tahoe! 🏂 I also enjoy hazy IPAs, Islay Scotches, road cycling, and playing Dota 2 poorly.\n\nPlease catch up with me on social media! Be aware that I'm awful at responding to DMs!!\n\n- [Twitter][x]\n- [Bluesky][bs]\n- [LinkedIn][li]\n- [GitHub][gh]\n\n## Where to File Issues\n\nI write these courses and take care to not make mistakes. However when teaching over ten hours of material, mistakes are inevitable, both here in the grammar and in the course with the material. However I (and the wonderful team at Frontend Masters) are constantly correcting the mistakes so that those of you that come later get the best product possible. If you find a mistake we'd love to fix it. The best way to do this is to [open a pull request or file an issue on the GitHub repo][issues]. While I'm always happy to chat and give advice on social media, I can't be tech support for everyone. And if you file it on GitHub, those who come later can Google the same answer you got.\n\n## How the repo works\n\nThere 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:\n\n```bash\ngit clone https://github.com/btholt/citr-v9-project.git\n```\n\nEvery 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!\n\nThe 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.\n\nWe'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.\n\n> And one last request! [Please star this repo][site]. It helps the course be more discoverable and with my fragile ego.\n\n[x]: https://twitter.com/holtbt\n[bs]: https://bsky.app/profile/brianholt.me\n[li]: https://www.linkedin.com/in/btholt/\n[gh]: https://github.com/btholt\n[site]: https://github.com/btholt/complete-intro-to-react-v9\n[projects]: https://github.com/btholt/citr-v9-project\n[issues]: https://github.com/btholt/complete-intro-to-react-v9/issues\n[neon]: https://neon.tech/\n[zip]: https://github.com/btholt/citr-v9-project/archive/refs/heads/main.zip","slug":"intro","title":"Intro","section":"Welcome","icon":"info-circle","filePath":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/01-welcome/A-intro.md","nextSlug":"/lessons/welcome/my-setup","prevSlug":null}},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/welcome/my-setup.json b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/welcome/my-setup.json new file mode 100644 index 0000000..73305ec --- /dev/null +++ b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/welcome/my-setup.json @@ -0,0 +1 @@ +{"pageProps":{"post":{"attributes":{"description":"Explore essential tools and configurations for the \"Complete Intro to React v9\" course by Brian Holt, including recommended Node.js version, Visual Studio Code setup with extensions, and terminal preferences for optimal React development experience. Learn about the use of tools like fnm for Node.js, Starship Prompt, MonoLisa font, and more within this comprehensive React course.","keywords":["Brian Holt","React","Node.js","Visual Studio Code","Starship Prompt"]},"html":"

    Node.js

    \n

    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.

    \n

    I use fnm to manage my Node.js versions (similar to nvm).

    \n

    I think this course would work with recent versions of bun or deno but it's untested. Beware if you decide go down this path.

    \n

    Tools FAQ

    \n

    What tools are your using?

    \n
      \n
    • Visual Studio Code – I used to work at Microsoft on VS Code so it's no surprise that I'll be using it in this course. We'll also be using a few extensions that I'll call out as we get there.
    • \n
    • Firefox – I want more than Chromium to exist so I support Firefox where I can. Feel free to use any browser; it won't matter in this course.
    • \n
    • Terminal.app – I used to use iTerm2 and Hyper but in the end I appreciate how fast the default terminal is.
    • \n
    \n

    What <font/theme/extension> are you using?

    \n
      \n
    • Visual Studio Code
        \n
      • Dark+ Theme – It comes installed by default but it's not the default theme anymore. I'm so used to it that I can't switch.
      • \n
      • MonoLisa font – I like fonts and I look at it all day so I was okay paying for it. I have ligatures enabled which is why you might see strange glyphs. If you want ligatures but don't want to pay, the linked ligature article has a few. I like Cascadia Code from Microsoft.
      • \n
      • vscode-icons – Lots of neat icons for VS Code and it's free.
      • \n
      \n
    • \n
    • Terminal
        \n
      • zsh – It comes with macOS now and I'm way too lazy to switch back to bash.
      • \n
      • Dracula theme – I like the pastels. I would use it in VS Code too if Dark+ wasn't ingrained in my blood.
      • \n
      • Starship Prompt – Very cool prompt that's just pretty. Also shows you what sort of project you're in which is occasionally useful
      • \n
      • CaskaydiaCove Nerd Font – This works with Starship prompt to give you the JS logos and all those extra glyphs. It's based on Cascadia Code.
      • \n
      \n
    • \n
    \n","markdown":"\n## Node.js\n\nYou'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.\n\nI use [fnm][fnm] to manage my Node.js versions (similar to nvm).\n\nI _think_ this course would work with recent versions of [bun][bun] or [deno][deno] but it's untested. Beware if you decide go down this path.\n\n## Tools FAQ\n\n### What tools are your using?\n\n- Visual Studio Code – I used to work at Microsoft on VS Code so it's no surprise that I'll be using it in this course. We'll also be using a few extensions that I'll call out as we get there.\n- Firefox – I want more than Chromium to exist so I support Firefox where I can. Feel free to use any browser; it won't matter in this course.\n- Terminal.app – I used to use iTerm2 and Hyper but in the end I appreciate how fast the default terminal is.\n\n### What are you using?\n\n- Visual Studio Code\n - Dark+ Theme – It comes installed by default but it's not the default theme anymore. I'm so used to it that I can't switch.\n - [MonoLisa][monolisa] font – I like fonts and I look at it all day so I was okay paying for it. I have [ligatures][ligatures] enabled which is why you might see strange glyphs. If you want ligatures but don't want to pay, the linked ligature article has a few. I like Cascadia Code from Microsoft.\n - [vscode-icons][vscode-icons] – Lots of neat icons for VS Code and it's free.\n- Terminal\n - zsh – It comes with macOS now and I'm _way_ too lazy to switch back to bash.\n - [Dracula theme][dracula] – I like the pastels. I would use it in VS Code too if Dark+ wasn't ingrained in my blood.\n - [Starship Prompt][starship] – Very cool prompt that's just pretty. Also shows you what sort of project you're in which is occasionally useful\n - [CaskaydiaCove Nerd Font][nerd] – This works with Starship prompt to give you the JS logos and all those extra glyphs. It's based on Cascadia Code.\n\n[ligatures]: https://worldofzero.com/posts/enable-font-ligatures-vscode/\n[monolisa]: https://www.monolisa.dev/\n[vscode-icons]: https://marketplace.visualstudio.com/items?itemName=vscode-icons-team.vscode-icons\n[dracula]: https://draculatheme.com/terminal\n[starship]: https://starship.rs/\n[nerd]: https://www.nerdfonts.com/font-downloads\n[fnm]: https://github.com/Schniz/fnm\n[bun]: https://bun.sh/\n[deno]: https://deno.com/\n","slug":"my-setup","title":"My Setup","section":"Welcome","icon":"info-circle","filePath":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/01-welcome/B-my-setup.md","nextSlug":"/lessons/no-frills-react/react-without-a-build-step","prevSlug":"/lessons/welcome/intro"}},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/whats-next/form-actions.json b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/whats-next/form-actions.json new file mode 100644 index 0000000..3534f6b --- /dev/null +++ b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/whats-next/form-actions.json @@ -0,0 +1 @@ +{"pageProps":{"post":{"attributes":{"description":"Learn how to upgrade your application to React 19 and simplify form handling in this segment of Brian Holt’s Complete Intro to React, version 9. Discover new React form actions, \"use server\" directives for server-side logic, and useFormData hooks to manage input states effectively. Ideal for React developers seeking practical techniques to streamline their web applications.","keywords":["React 19","form actions","useFormData","Brian Holt","React upgrade"]},"html":"

    Form Actions

    \n

    Okay, so let's actually upgrade our app to be React 19.

    \n
    \n

    🚨 Pay attention here, as you need to figure out which version of React to install.

    \n
    \n

    Go here and look at the versions of React published on npm. If you see that React 19 is still in canary/rc/next (which are all the same) then you can run npm i react@rc react-dom@rc. You'll get a lot of warnings about things not matching versions in your command-line, that's totally okay.

    \n

    If React 19 is latest, then run npm install react@19 react-dom@19 and then follow these instructions. Be extra cautious because things may have changed!!

    \n

    Okay, let's talk form actions. A lot of web interfaces is just handling form inputs, and so the React team decided to make it easier to do that. It's actually really similar to what we've done already, just a little less boilerplate. Open your contact.lazy.jsx and let's modify this page to use a form action.

    \n
    mutationFn: function (formData) { // change to formData\n  // remove e.preventDefault\n  // remove formData constructor\n  return postContact(\n    formData.get("name"),\n    formData.get("email"),\n    formData.get("message"),\n  );\n},\n\n// change onSubmit to action\n<form action={mutation.mutate}>\n

    That's it! It really is just a convenience function to make handling for submits even easier to do. There's nothing wrong with what we had either, and that will continue to work as-is too.

    \n

    Let's go convert order.lazy.jsx too

    \n
    // extract submission function from form\nfunction addToCart() {\n  setCart([...cart, { pizza: selectedPizza, size: pizzaSize, price }]);\n}\n// update to action\n<form action={addToCart}>[…]</form>;\n

    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

    \n
    function addToCart(formData) {\n  "use server";\n  sql(`INSERT INTO cart (user_id, pizza_type, size) VALUES ($1, $2)`, [\n    formData.pizza_type,\n    formData.size,\n  ]);\n}\n

    Since it's on the server, we can now safely insert into our database, directly from inside our React component. React/Next.js will handle all the details of handling the execution of that on server. Pretty cool, right? We'll talk more about this in Intermediate React v6.

    \n

    Let's do one more cool trick here. There's a new hook called useFormData that lets children components see if they're inside of a form being submitted without having to pass lots of data around.

    \n

    In contact.lazy.jsx

    \n
    // at top\nimport { useFormStatus } from "react-dom"; // note react-dom, not react\n\n// replace the two inputs\n<ContactInput name="name" type="text" placeholder="Name" />\n<ContactInput name="email" type="email" placeholder="Email" />\n\n// at the bottom\nfunction ContactInput(props) {\n  const { pending } = useFormStatus();\n  return (\n    <input\n      disabled={pending}\n      name={props.name}\n      type={props.type}\n      placeholder={props.placeholder}\n    />\n  );\n}\n
      \n
    • This is a silly example, but imagine you had really complicated inputs that had a design system, tool tips, and all sorts of other UIs. This is common in large React codebases. This could be really useful for that.
    • \n
    • As a side note, if I have really small components like ContactInput, I'll just stick in the same file like we did here. It can be a useful pattern. Some people are purists and demand one file, one component. I am not a purist.
    • \n
    • We didn't do the button or the text area just for brevity's sake but you could.
    • \n
    • Since we're local, this pending state will be super short. But you will notice the background flash gray. That's the disabled state.
    • \n
    \n

    And that's form actions!

    \n
    \n

    🏁 Click here to see the state of the project up until now: 15-form-actions

    \n
    \n","markdown":"\n## Form Actions\n\nOkay, so let's actually upgrade our app to be React 19.\n\n> 🚨 Pay attention here, as you need to figure out which version of React to install.\n\n[Go here][npm] and look at the versions of React published on npm. If you see that React 19 is still in canary/rc/next (which are all the same) then you can run `npm i react@rc react-dom@rc`. You'll get a lot of warnings about things not matching versions in your command-line, that's totally okay.\n\nIf React 19 is latest, then run `npm install react@19 react-dom@19` and then follow these instructions. Be extra cautious because things may have changed!!\n\nOkay, let's talk form actions. A lot of web interfaces is just handling form inputs, and so the React team decided to make it easier to do that. It's actually really similar to what we've done already, just a little less boilerplate. Open your contact.lazy.jsx and let's modify this page to use a form action.\n\n```javascript\nmutationFn: function (formData) { // change to formData\n // remove e.preventDefault\n // remove formData constructor\n return postContact(\n formData.get(\"name\"),\n formData.get(\"email\"),\n formData.get(\"message\"),\n );\n},\n\n// change onSubmit to action\n
    \n```\n\nThat's it! It really is just a convenience function to make handling for submits even easier to do. There's nothing wrong with what we had either, and that will continue to work as-is too.\n\nLet's go convert order.lazy.jsx too\n\n```javascript\n// extract submission function from form\nfunction addToCart() {\n setCart([...cart, { pizza: selectedPizza, size: pizzaSize, price }]);\n}\n// update to action\n[…]
    ;\n```\n\nSame 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\n\n```javascript\nfunction addToCart(formData) {\n \"use server\";\n sql(`INSERT INTO cart (user_id, pizza_type, size) VALUES ($1, $2)`, [\n formData.pizza_type,\n formData.size,\n ]);\n}\n```\n\nSince it's on the server, we can now safely insert into our database, directly from inside our React component. React/Next.js will handle all the details of handling the execution of that on server. Pretty cool, right? We'll talk more about this in Intermediate React v6.\n\nLet's do one more cool trick here. There's a new hook called `useFormData` that lets children components see if they're inside of a form being submitted without having to pass lots of data around.\n\nIn contact.lazy.jsx\n\n```javascript\n// at top\nimport { useFormStatus } from \"react-dom\"; // note react-dom, not react\n\n// replace the two inputs\n\n\n\n// at the bottom\nfunction ContactInput(props) {\n const { pending } = useFormStatus();\n return (\n \n );\n}\n```\n\n- This is a silly example, but imagine you had really complicated inputs that had a design system, tool tips, and all sorts of other UIs. This is common in large React codebases. This could be really useful for that.\n- As a side note, if I have really small components like ContactInput, I'll just stick in the same file like we did here. It can be a useful pattern. Some people are purists and demand one file, one component. I am not a purist.\n- We didn't do the button or the text area just for brevity's sake but you could.\n- Since we're local, this pending state will be super short. But you will notice the background flash gray. That's the disabled state.\n\nAnd that's form actions!\n\n> 🏁 [Click here to see the state of the project up until now: 15-form-actions][step]\n\n[step]: https://github.com/btholt/citr-v9-project/tree/master/15-form-actions\n[npm]: https://www.npmjs.com/package/react?activeTab=versions","slug":"form-actions","title":"Form Actions","section":"Whats Next","icon":"shuttle-space","filePath":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/08-whats-next/B-form-actions.md","nextSlug":"/lessons/whats-next/use-and-suspense","prevSlug":"/lessons/whats-next/react-19"}},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/whats-next/react-19.json b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/whats-next/react-19.json new file mode 100644 index 0000000..f924891 --- /dev/null +++ b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/whats-next/react-19.json @@ -0,0 +1 @@ +{"pageProps":{"post":{"attributes":{"description":"Explore the Complete Intro to React (v9) course by Brian Holt, focusing on core React concepts and upcoming React 19 features like 'use client' and 'use server'. Gain insights into new capabilities such as putting elements in the head, preload/preconnect hints, and seamless integration of custom components in React development.","keywords":["Brian Holt","React 19","React development","web components","use client"]},"html":"

    React 19

    \n

    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.

    \n

    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.

    \n

    So, given React 19 is about out, and we know 95% of what is going to be in it, we are going to cover a few features so that you can be prepared for it. Do remember, we are going to use the React 19 release candidate, which means while conceptually that are very unlikely to change big concepts, they may tweak the precise API of how it works. Just be aware that it may change a bit.

    \n

    First thing, let's discuss a few features that are cool but not worth us spending a lot of time writing code for. Then we'll cover a few that we'll actually add code to our app for.

    \n

    'use client' and 'use server'

    \n

    Let's start with one of the bigger shifts in React. We will cover this extensively in Intermediate React so if you are curious, check that course out when it comes out. Essentially we can indicate to React "hey, only run this component on the server" or likewise, "hey, only render this client side". Why would you do that?

    \n

    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?

    \n

    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.

    \n

    '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.

    \n

    Putting stuff in the head

    \n

    Imagine you want your React component to add <link>, <meta>, <title> or other HTML pages in the doc. Right now you either need to use a portal or you need something like react-helmet to do that. With React 19, you just need to render the tag and it will automatically stick it in the header.

    \n
    const MyPizza = () => (\n  <div>\n    <h1>I love pizza</h1>\n    <title>🍕 Brian Loves Pizza 🍕</title>\n  </div>\n);\n

    Now whenever MyPizza gets rendered it will change the title of the doc (which what shows up in browser tab).

    \n

    preload and preconnect

    \n

    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 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.

    \n
    const EnterConfirmation = () => {\n  preload("https://example.com/text-editor.js", { as: "script" });\n  return (\n    <div>\n      <a href="/text-editor">Click here to open the text editor</a>\n    </div>\n  );\n};\n

    Custom components (aka web components)

    \n

    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.

    \n

    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.

    \n

    More stuff!

    \n

    Click here to read the official React 19 blog post if you want to read more. Lots of cool stuff coming!!

    \n","markdown":"\n## React 19\n\nThis 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.\n\nDo 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.\n\nSo, given React 19 is about out, and we know 95% of what is going to be in it, we are going to cover a few features so that you can be prepared for it. Do remember, we are going to use the React 19 release candidate, which means while conceptually that are very unlikely to change big concepts, they may tweak the precise API of how it works. Just be aware that it may change a bit.\n\nFirst thing, let's discuss a few features that are cool but not worth us spending a lot of time writing code for. Then we'll cover a few that we'll actually add code to our app for.\n\n## 'use client' and 'use server'\n\nLet's start with one of the bigger shifts in React. We will cover this _extensively_ in Intermediate React so if you are curious, check that course out when it comes out. Essentially we can indicate to React \"hey, only run this component on the server\" or likewise, \"hey, only render this client side\". Why would you do that?\n\nFor 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?\n\nThey'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.\n\n'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.\n\n## Putting stuff in the head\n\nImagine you want your React component to add ``, ``, `` or other HTML pages in the doc. Right now you either need to use a portal or you need something like [react-helmet][helmet] to do that. With React 19, you just need to render the tag and it will automatically stick it in the header.\n\n```javascript\nconst MyPizza = () => (\n <div>\n <h1>I love pizza</h1>\n <title>🍕 Brian Loves Pizza 🍕\n \n);\n```\n\nNow whenever MyPizza gets rendered it will change the title of the doc (which what shows up in browser tab).\n\n## preload and preconnect\n\nTwo 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.\n\n```javascript\nconst EnterConfirmation = () => {\n preload(\"https://example.com/text-editor.js\", { as: \"script\" });\n return (\n \n );\n};\n```\n\n## Custom components (aka web components)\n\nIt'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.\n\nNow 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.\n\n## More stuff!\n\n[Click here][react19] to read the official React 19 blog post if you want to read more. Lots of cool stuff coming!!\n\n[helmet]: https://github.com/nfl/react-helmet\n[preload]: https://www.debugbear.com/blog/resource-hints-rel-preload-prefetch-preconnect\n[react19]: https://react.dev/blog/2024/04/25/react-19\n","slug":"react-19","title":"React 19","section":"Whats Next","icon":"shuttle-space","filePath":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/08-whats-next/A-react-19.md","nextSlug":"/lessons/whats-next/form-actions","prevSlug":"/lessons/testing/browser-tests"}},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/whats-next/react-compiler.json b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/whats-next/react-compiler.json new file mode 100644 index 0000000..be3c29f --- /dev/null +++ b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/whats-next/react-compiler.json @@ -0,0 +1 @@ +{"pageProps":{"post":{"attributes":{"description":"Learn about performance optimization in React with tools like useMemo and useCallback, and discover how the React Compiler can automate these optimizations for you. This guide provides practical steps for integrating React Compiler into your development workflow, ensuring improved performance with minimal effort.","keywords":["React Compiler","React performance","useMemo","useCallback","optimization","React development"]},"html":"

    React Compiler

    \n

    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.

    \n

    Specifically I'm talking about useMemo and useCallback. 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.

    \n

    Enter React Compiler (formerly known as React Forget). The React team at Facebook is basically going to do these optimizations for you. It's going to detect "hey, this component shouldn't ever change, I'm going to put useMemo on it for you." I love this as it's free performance with zero thought on behalf of the developer. It's pretty conservative about when it does this, so it should work without a hitch.

    \n
    \n

    If you do ever hit a case where it is memoizing something and you don't want it do, you can stick "use no memo"; at the top of a file and the React Compiler will skip it.

    \n
    \n

    So let's run the checker on the app.

    \n
    npx react-compiler-healthcheck@beta\n

    Our app is good to go! You can run this on your codebase too and see if it will work on your code.

    \n

    Let's go ahead and install the plugin.

    \n
    # also prone to change as they're rapidly iterating on this\nnpm i -D babel-plugin-react-compiler@beta --force\n

    Now add this to your vite.config.js

    \n
    // replace react()\nreact({\n  babel: {\n    plugins: [\n      [\n        "babel-plugin-react-compiler",\n        {\n          target: "19",\n        },\n      ],\n    ],\n  },\n}),\n

    Now let's run your app! If everything is done right, you will notice nothing. It will look no different. But open your React dev tools and look through your components. You should see lots of components have "Memo ✨" next to them. These are the components the React Compiler was able to automatically optimize for you.

    \n

    And that's it! Over time this will get lots better. The React team is saying this is safe to try on your codebase today, so you might try it and see if it works for you. I know they'd love feedback on their GitHub repo.

    \n

    If you want to learn more, I'd strongly suggest Lauren Tan's talk from React Conf 2024. She was/is the eng manager over React Compiler.

    \n
    \n

    🏁 Click here to see the state of the project up until now: 17-react-compiler

    \n
    \n","markdown":"\n## React Compiler\n\nWe 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.\n\nSpecifically 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.\n\nEnter React Compiler (formerly known as React Forget). The React team at Facebook is basically going to do these optimizations for you. It's going to detect \"hey, this component shouldn't ever change, I'm going to put useMemo on it for you.\" I love this as it's free performance with zero thought on behalf of the developer. It's pretty conservative about when it does this, so it should work without a hitch.\n\n> If you do ever hit a case where it is memoizing something and you don't want it do, you can stick \"use no memo\"; at the top of a file and the React Compiler will skip it.\n\nSo let's run the checker on the app.\n\n```bash\nnpx react-compiler-healthcheck@beta\n```\n\nOur app is good to go! You can run this on your codebase too and see if it will work on your code.\n\nLet's go ahead and install the plugin.\n\n```bash\n# also prone to change as they're rapidly iterating on this\nnpm i -D babel-plugin-react-compiler@beta --force\n```\n\nNow add this to your vite.config.js\n\n```javascript\n// replace react()\nreact({\n babel: {\n plugins: [\n [\n \"babel-plugin-react-compiler\",\n {\n target: \"19\",\n },\n ],\n ],\n },\n}),\n```\n\nNow let's run your app! If everything is done right, you will notice _nothing_. It will look no different. But open your React dev tools and look through your components. You should see lots of components have \"Memo ✨\" next to them. These are the components the React Compiler was able to automatically optimize for you.\n\nAnd that's it! Over time this will get lots better. The React team is saying this is safe to try on your codebase today, so you might try it and see if it works for you. I know they'd love feedback on their GitHub repo.\n\nIf you want to learn more, I'd strongly suggest [Lauren Tan's talk from React Conf 2024][lauren]. She was/is the eng manager over React Compiler.\n\n> 🏁 [Click here to see the state of the project up until now: 17-react-compiler][step]\n\n[step]: https://github.com/btholt/citr-v9-project/tree/master/17-react-compiler\n[callback]: https://react.dev/reference/react/useCallback\n[memo]: https://react.dev/reference/react/useMemo\n[lauren]: https://www.youtube.com/live/T8TZQ6k4SLE?feature=shared&t=12020\n","slug":"react-compiler","title":"React Compiler","section":"Whats Next","icon":"shuttle-space","filePath":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/08-whats-next/D-react-compiler.md","nextSlug":"/lessons/wrap-up/congrats","prevSlug":"/lessons/whats-next/use-and-suspense"}},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/whats-next/use-and-suspense.json b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/whats-next/use-and-suspense.json new file mode 100644 index 0000000..2cbfc3b --- /dev/null +++ b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/whats-next/use-and-suspense.json @@ -0,0 +1 @@ +{"pageProps":{"post":{"attributes":{"title":"use and Suspense","description":"Explore React Suspense and the new 'use' hook in React 19, alongside TanStack Query integration, to manage component rendering and data loading seamlessly. Learn how to implement Suspense to improve code readability and handling of async operations in React applications, guided by seasoned developer Brian Holt.","keywords":["React Suspense","use hook","TanStack Query","React 19","async rendering"]},"html":"

    use and Suspense

    \n

    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.)

    \n

    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.

    \n

    This is what the idea of the new use hook is from React. You say use(myPromise) and the React component will only render once myPromise has rendered.

    \n

    Luckily TanStack Query already has a few ways of doing this, so we're going to use that. But just know that use works with any promise, not just TanStack Query.

    \n

    First open App.jsx, we have to enable the experimental support for this in TanStack Query (you may not need to this in the future.)

    \n
    // replace queryClient\nconst queryClient = new QueryClient({\n  defaultOptions: {\n    queries: {\n      experimental_prefetchInRender: true,\n    },\n  },\n});\n

    Now in past.lazy.jsx, we need to do some refactoring.

    \n
    import { Suspense, useState, use } from "react"; // import Suspense and use\n\n// move query and page hooks to ErrorBoundary component\nfunction ErrorBoundaryWrappedPastOrderRoutes() {\n  const [page, setPage] = useState(1);\n  const loadedPromise = useQuery({\n    queryKey: ["past-orders", page],\n    queryFn: () => getPastOrders(page),\n    staleTime: 30000,\n  }).promise;\n  return (\n    <ErrorBoundary>\n      <Suspense\n        fallback={\n          <div className="past-orders">\n            <h2>Loading Past Orders …</h2>\n          </div>\n        }\n      >\n        <PastOrdersRoute\n          loadedPromise={loadedPromise}\n          page={page}\n          setPage={setPage}\n        />\n      </Suspense>\n    </ErrorBoundary>\n  );\n}\n\n// first line in PastOrderRoute\nconst data = use(loadedPromise);\n// also, we had to remove the useQuery call to getPastOrders (with an S), and the related if (isLoading) return statement\n
      \n
    • We use Suspense to tell React where to pause rendering until everything in it has resolved. If you have several things that suspend, this won't render until they all complete.
    • \n
    • fallback tells it what to render while it's suspended
    • \n
    • We need to query inside the parent component (and thus keep track of the page too) because the promise needs to exist outside of the component that is getting suspended. Otherwise it'd be freshly called every single your component rendered. Similar to error boundaries.
    • \n
    • We shoved everything in our error boundary wrapping component. This should probably be renamed.
    • \n
    • Again, we're using TanStack Query's support for this, but it could be any promise. TanStack Query just made it easy.
    • \n
    • TanStack Query has several other ways of doing Suspense. We just wanted to try the use hook.
    • \n
    • use has a few other uses. Check it out in the docs.
    • \n
    • Unlike most hooks, use, can be used in conditionals and for loops. Kinda weird given most of the rules around hooks, but still true.
    • \n
    \n
    \n

    🏁 Click here to see the state of the project up until now: 16-use

    \n
    \n","markdown":"\n## use and Suspense\n\nWe'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.)\n\nThe 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.\n\nThis is what the idea of the new `use` hook is from React. You say `use(myPromise)` and the React component will only render once myPromise has rendered.\n\nLuckily TanStack Query already has a few ways of doing this, so we're going to use that. But just know that `use` works with any promise, not just TanStack Query.\n\nFirst open App.jsx, we have to enable the experimental support for this in TanStack Query (you may not need to this in the future.)\n\n```javascript\n// replace queryClient\nconst queryClient = new QueryClient({\n defaultOptions: {\n queries: {\n experimental_prefetchInRender: true,\n },\n },\n});\n```\n\nNow in past.lazy.jsx, we need to do some refactoring.\n\n```javascript\nimport { Suspense, useState, use } from \"react\"; // import Suspense and use\n\n// move query and page hooks to ErrorBoundary component\nfunction ErrorBoundaryWrappedPastOrderRoutes() {\n const [page, setPage] = useState(1);\n const loadedPromise = useQuery({\n queryKey: [\"past-orders\", page],\n queryFn: () => getPastOrders(page),\n staleTime: 30000,\n }).promise;\n return (\n \n \n

    Loading Past Orders …

    \n \n }\n >\n \n \n
    \n );\n}\n\n// first line in PastOrderRoute\nconst data = use(loadedPromise);\n// also, we had to remove the useQuery call to getPastOrders (with an S), and the related if (isLoading) return statement\n```\n\n- We use Suspense to tell React where to pause rendering until _everything_ in it has resolved. If you have several things that suspend, this won't render until they all complete.\n- fallback tells it what to render while it's suspended\n- We need to query inside the parent component (and thus keep track of the page too) because the promise needs to exist outside of the component that is getting suspended. Otherwise it'd be freshly called every single your component rendered. Similar to error boundaries.\n- We shoved everything in our error boundary wrapping component. This should probably be renamed.\n- Again, we're using TanStack Query's support for this, but it could be any promise. TanStack Query just made it easy.\n- TanStack Query [has several other ways of doing Suspense][tsq-suspense]. We just wanted to try the `use` hook.\n- [use][use] has a few other uses. Check it out in the docs.\n- Unlike most hooks, use, can be used in conditionals and for loops. Kinda weird given most of the rules around hooks, but still true.\n\n> 🏁 [Click here to see the state of the project up until now: 16-use][step]\n\n[step]: https://github.com/btholt/citr-v9-project/tree/master/16-use\n[tsq-suspense]: https://tanstack.com/query/v5/docs/framework/react/guides/suspense\n[use]: https://react.dev/reference/react/use\n","slug":"use-and-suspense","title":"use and Suspense","section":"Whats Next","icon":"shuttle-space","filePath":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/08-whats-next/C-use-and-suspense.md","nextSlug":"/lessons/whats-next/react-compiler","prevSlug":"/lessons/whats-next/form-actions"}},"__N_SSG":true} \ No newline at end of file diff --git a/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/wrap-up/congrats.json b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/wrap-up/congrats.json new file mode 100644 index 0000000..31a4a4f --- /dev/null +++ b/_next/data/PxJrDJjmFmsKzZJwY10Qb/lessons/wrap-up/congrats.json @@ -0,0 +1 @@ +{"pageProps":{"post":{"attributes":{"description":"Congratulations on completing the Complete Intro to React v9 course by Brian Holt! You're ready to develop React applications professionally, with skills applicable to real-world tech jobs. Consider advancing your knowledge with Intermediate React v5, exploring server-side React techniques, or diving into hands-on projects to solidify your understanding.","keywords":["React","React course","Brian Holt","Intermediate React","React development"]},"html":"

    Congratulations!! You did it!

    \n

    This is a lot of material to take in, so congrats on sticking with it. Hopefully it was fun and maybe made you support your local pizzeria along the way!

    \n

    You are now fully equipped to write React professionally. Everything you learned here is 95%+ of what you would need to write React at any tech company. The last 5% is what I'll teach you in Intermediate React v6 which has more to do with server-side React and advance performance techniques. But for now, this is almost all you need to know!

    \n

    So what should you do from here?

    \n
      \n
    • Intermediate React 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.
    • \n
    • Follow the rest of the Frontend Masters React Learning Path.
    • \n
    • 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!
        \n
      • An e-commerce website to sell single socks so you can match unmatched socks
      • \n
      • An AI prompt text editor
      • \n
      • A music app that lets you add Spotify, Apple Music, and Tidal songs to it.
      • \n
      • A client to let you read Thread, X, and Bluesky in the same app.
      • \n
      \n
    • \n
    • Learn Next.js, Remix/React-Router (soon those will be the same thing) or some other way of doing React on the server
    • \n
    • Learn React + Tailwind
    • \n
    \n

    There are lots of way to go with this and they're all fun! I hope you enjoyed the course and I look forward to hopefully getting to teach with you again soon!

    \n","markdown":"\nCongratulations!! You did it!\n\nThis is a lot of material to take in, so congrats on sticking with it. Hopefully it was fun and maybe made you support your local pizzeria along the way!\n\nYou are now fully equipped to write React professionally. Everything you learned here is 95%+ of what you would need to write React at any tech company. The last 5% is what I'll teach you in [Intermediate React v6][v6] which has more to do with server-side React and advance performance techniques. But for now, this is almost all you need to know!\n\nSo what should you do from here?\n\n- [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.\n- Follow the rest of the [Frontend Masters React Learning Path][path].\n- 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!\n - An e-commerce website to sell single socks so you can match unmatched socks\n - An AI prompt text editor\n - A music app that lets you add Spotify, Apple Music, and Tidal songs to it.\n - A client to let you read Thread, X, and Bluesky in the same app.\n- Learn [Next.js][next], Remix/React-Router (soon those will be the same thing) or some other way of doing React on the server\n- Learn React + [Tailwind][tailwind]\n\nThere are lots of way to go with this and they're all fun! I hope you enjoyed the course and I look forward to hopefully getting to teach with you again soon!\n\n[v6]: https://frontendmasters.com/workshops/intermediate-react-v6/\n[v5]: https://frontendmasters.com/courses/intermediate-react-v5/\n[path]: https://frontendmasters.com/learn/react/\n[next]: https://frontendmasters.com/topics/next-js/\n[tailwind]: https://frontendmasters.com/courses/tailwind-css/","slug":"congrats","title":"Congrats","section":"Wrap Up","icon":"hands-clapping","filePath":"/home/runner/work/complete-intro-to-react-v9/complete-intro-to-react-v9/lessons/09-wrap-up/A-congrats.md","nextSlug":null,"prevSlug":"/lessons/whats-next/react-compiler"}},"__N_SSG":true} \ No newline at end of file diff --git a/_next/static/PxJrDJjmFmsKzZJwY10Qb/_buildManifest.js b/_next/static/PxJrDJjmFmsKzZJwY10Qb/_buildManifest.js new file mode 100644 index 0000000..d544382 --- /dev/null +++ b/_next/static/PxJrDJjmFmsKzZJwY10Qb/_buildManifest.js @@ -0,0 +1 @@ +self.__BUILD_MANIFEST={__rewrites:{afterFiles:[],beforeFiles:[],fallback:[]},"/":["static/chunks/pages/index-2a6b1d427e655e1b.js"],"/_error":["static/chunks/pages/_error-77823ddac6993d35.js"],"/lessons/[section]/[slug]":["static/chunks/pages/lessons/[section]/[slug]-f133ec3e69124099.js"],sortedPages:["/","/_app","/_error","/lessons/[section]/[slug]"]},self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB(); \ No newline at end of file diff --git a/_next/static/PxJrDJjmFmsKzZJwY10Qb/_ssgManifest.js b/_next/static/PxJrDJjmFmsKzZJwY10Qb/_ssgManifest.js new file mode 100644 index 0000000..10f162a --- /dev/null +++ b/_next/static/PxJrDJjmFmsKzZJwY10Qb/_ssgManifest.js @@ -0,0 +1 @@ +self.__SSG_MANIFEST=new Set(["\u002F","\u002Flessons\u002F[section]\u002F[slug]"]);self.__SSG_MANIFEST_CB&&self.__SSG_MANIFEST_CB() \ No newline at end of file diff --git a/_next/static/chunks/framework-ecc4130bc7a58a64.js b/_next/static/chunks/framework-ecc4130bc7a58a64.js new file mode 100644 index 0000000..3b13c66 --- /dev/null +++ b/_next/static/chunks/framework-ecc4130bc7a58a64.js @@ -0,0 +1,33 @@ +"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[774],{4448:function(e,n,t){/** + * @license React + * react-dom.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var r,l,a,u,o,i,s=t(7294),c=t(3840);function f(e){for(var n="https://reactjs.org/docs/error-decoder.html?invariant="+e,t=1;t