generated from btholt/next-course-starter
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit a4e1d49
Showing
101 changed files
with
2,196 additions
and
0 deletions.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
<!DOCTYPE html><html><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon.png"/><link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32.png"/><link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16.png"/><link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16.png"/><link rel="icon" type="image/x-icon" href="/images/favicon.ico"/><title>404: This page could not be found</title><meta name="next-head-count" content="8"/><link data-next-font="size-adjust" rel="preconnect" href="/" crossorigin="anonymous"/><link rel="preload" href="/_next/static/css/844ac165d3042edb.css" as="style"/><link rel="stylesheet" href="/_next/static/css/844ac165d3042edb.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" nomodule="" src="/_next/static/chunks/polyfills-78c92fac7aa8fdd8.js"></script><script src="/_next/static/chunks/webpack-4e7214a60fad8e88.js" defer=""></script><script src="/_next/static/chunks/framework-ecc4130bc7a58a64.js" defer=""></script><script src="/_next/static/chunks/main-a6a08e1f60796d6f.js" defer=""></script><script src="/_next/static/chunks/pages/_app-eac778379b538880.js" defer=""></script><script src="/_next/static/chunks/pages/_error-77823ddac6993d35.js" defer=""></script><script src="/_next/static/DcfDDggYt8CL6tFRs7qgL/_buildManifest.js" defer=""></script><script src="/_next/static/DcfDDggYt8CL6tFRs7qgL/_ssgManifest.js" defer=""></script></head><body><div id="__next"><div class="remix-app"><header class="navbar"><h1 class="navbar-brand"><a href="/">Complete Intro to React</a></h1><div class="navbar-info"><a href="https://frontendmasters.com/courses/complete-react-v9/" class="cta-btn">Watch on Frontend Masters</a></div></header><div class="content-container"><div class="main"><div style="font-family:system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div style="line-height:48px"><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding-right:23px;font-size:24px;font-weight:500;vertical-align:top">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:28px">This page could not be found<!-- -->.</h2></div></div></div></div></div><script async="" defer="" src="https://a.holt.courses/latest.js"></script><noscript><img src="https://a.holt.courses/noscript.gif" alt="" referrerPolicy="no-referrer-when-downgrade"/></noscript><footer class="footer"><ul class="socials"><li class="social"><a href="https://twitter.com/holtbt"><svg fill="none" height="auto" width="32" xmlns="http://www.w3.org/2000/svg" viewBox="0.254 0.25 500 451.95400000000006"><path d="M394.033.25h76.67L303.202 191.693l197.052 260.511h-154.29L225.118 294.205 86.844 452.204H10.127l179.16-204.77L.254.25H158.46l109.234 144.417zm-26.908 406.063h42.483L135.377 43.73h-45.59z" fill="var(--footer-icons)"></path></svg></a></li><li class="social"><a href="https://bsky.app/profile/brianholt.me"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -3.268 64 68.414" width="38" height="auto"><path fill="var(--footer-icons)" d="M13.873 3.805C21.21 9.332 29.103 20.537 32 26.55v15.882c0-.338-.13.044-.41.867-1.512 4.456-7.418 21.847-20.923 7.944-7.111-7.32-3.819-14.64 9.125-16.85-7.405 1.264-15.73-.825-18.014-9.015C1.12 23.022 0 8.51 0 6.55 0-3.268 8.579-.182 13.873 3.805zm36.254 0C42.79 9.332 34.897 20.537 32 26.55v15.882c0-.338.13.044.41.867 1.512 4.456 7.418 21.847 20.923 7.944 7.111-7.32 3.819-14.64-9.125-16.85 7.405 1.264 15.73-.825 18.014-9.015C62.88 23.022 64 8.51 64 6.55c0-9.818-8.578-6.732-13.873-2.745z"></path></svg></a></li><li class="social"><a href="https://github.com/btholt"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32" viewBox="0 0 32 32"><defs><clipPath id="clip-github-social"><rect width="32" height="32"></rect></clipPath></defs><g id="github-social" clip-path="url(#clip-github-social)"><g id="Group_272" data-name="Group 272" transform="translate(13522.5 -6994)"><path id="Subtraction_33" data-name="Subtraction 33" d="M-24967.5,8041a15.9,15.9,0,0,1-11.312-4.688A15.893,15.893,0,0,1-24983.5,8025a15.893,15.893,0,0,1,4.689-11.315A15.894,15.894,0,0,1-24967.5,8009a15.894,15.894,0,0,1,11.313,4.686A15.893,15.893,0,0,1-24951.5,8025a15.893,15.893,0,0,1-4.689,11.313A15.9,15.9,0,0,1-24967.5,8041Zm-3.781-4.571h0v3.918h7.895v-6.665a1.836,1.836,0,0,0-1.2-1.718c5.1-.617,7.467-2.975,7.467-7.424a7.176,7.176,0,0,0-1.637-4.728,6.74,6.74,0,0,0,.275-1.812,4.34,4.34,0,0,0-.52-2.452.574.574,0,0,0-.359-.1c-1.061,0-3.465,1.411-3.936,1.694a16.644,16.644,0,0,0-4.2-.489,16.379,16.379,0,0,0-3.969.445c-.846-.5-2.91-1.649-3.859-1.649a.566.566,0,0,0-.354.095,4.3,4.3,0,0,0-.521,2.452,6.7,6.7,0,0,0,.244,1.718,7.346,7.346,0,0,0-1.6,4.822,7.263,7.263,0,0,0,1.533,4.985c1.193,1.359,3.115,2.165,5.871,2.464a1.826,1.826,0,0,0-1.129,1.693v.5h0l-.006,0a7.121,7.121,0,0,1-2.033.363,2.608,2.608,0,0,1-.965-.158,4.438,4.438,0,0,1-1.836-1.881,2.361,2.361,0,0,0-1.248-1.091,3.472,3.472,0,0,0-1.217-.3.584.584,0,0,0-.545.224.282.282,0,0,0,.027.367,1.875,1.875,0,0,0,.447.307,4.732,4.732,0,0,1,.561.355,10.726,10.726,0,0,1,1.682,2.755c.043.092.078.163.105.217a3.876,3.876,0,0,0,2.42,1.185,6.036,6.036,0,0,0,.607.025c.875,0,1.988-.124,2-.125Z" transform="translate(11461 -1015)" fill="var(--footer-icons)"></path><g id="Ellipse_670" data-name="Ellipse 670" transform="translate(-13522.5 6994)" fill="none" stroke="var(--footer-icons)" stroke-width="1"><circle cx="16" cy="16" r="16" stroke="none"></circle><circle cx="16" cy="16" r="15.5" fill="none"></circle></g></g></g></svg></a></li><li class="social"><a href="https://linkedin.com/in/btholt"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32" viewBox="0 0 32 32"><defs><clipPath id="clip-linkedin-social"><rect width="32" height="32"></rect></clipPath></defs><g id="linkedin-social" clip-path="url(#clip-linkedin-social)"><g id="Group_270" data-name="Group 270" transform="translate(-86.349 -633.073)"><path id="Path_375" data-name="Path 375" d="M115.789,633.073a2.324,2.324,0,0,1,1.682.676,2.194,2.194,0,0,1,.695,1.627V662.8a2.131,2.131,0,0,1-.695,1.609,2.314,2.314,0,0,1-1.646.659H88.69a2.307,2.307,0,0,1-1.646-.659,2.128,2.128,0,0,1-.695-1.609V635.376a2.19,2.19,0,0,1,.695-1.627,2.322,2.322,0,0,1,1.682-.676h27.063Zm-20.224,9.672a2.561,2.561,0,0,0,0-3.584,2.658,2.658,0,0,0-1.938-.712,2.724,2.724,0,0,0-1.957.712,2.371,2.371,0,0,0-.75,1.792,2.4,2.4,0,0,0,.731,1.792,2.605,2.605,0,0,0,1.9.713h.037A2.7,2.7,0,0,0,95.565,642.745ZM96,645.434H91.213V659.88H96Zm17.3,6.144a7.007,7.007,0,0,0-1.573-4.9,5.68,5.68,0,0,0-6.839-.769,5.663,5.663,0,0,0-1.426,1.573v-2.048H98.674q.036.841,0,7.717v6.728h4.791V651.8a3.592,3.592,0,0,1,.146-1.17,2.913,2.913,0,0,1,.878-1.206,2.429,2.429,0,0,1,1.609-.549,2.108,2.108,0,0,1,1.865.914,4.265,4.265,0,0,1,.549,2.341v7.752H113.3Z" fill="var(--footer-icons)"></path></g></g></svg></a></li><li class="social"><div class="terms"><p>Content Licensed Under CC-BY-NC-4.0</p><p>Code Samples and Exercises Licensed Under Apache 2.0</p><p>Site Designed by<!-- --> <a href="https://www.alexdanielson.com/">Alex Danielson</a></p></div></li></ul></footer></div><script async="" defer="" src="https://a.holt.courses/latest.js"></script><noscript><img src="https://a.holt.courses/noscript.gif" alt="" referrerPolicy="no-referrer-when-downgrade"/></noscript></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"statusCode":404}},"page":"/_error","query":{},"buildId":"DcfDDggYt8CL6tFRs7qgL","nextExport":true,"isFallback":false,"gip":true,"scriptLoader":[]}</script></body></html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
react-v9.holt.courses |
Large diffs are not rendered by default.
Oops, something went wrong.
1 change: 1 addition & 0 deletions
1
_next/data/DcfDDggYt8CL6tFRs7qgL/lessons/advanced-react/error-boundaries.json
Large diffs are not rendered by default.
Oops, something went wrong.
1 change: 1 addition & 0 deletions
1
_next/data/DcfDDggYt8CL6tFRs7qgL/lessons/advanced-react/portals.json
Large diffs are not rendered by default.
Oops, something went wrong.
1 change: 1 addition & 0 deletions
1
_next/data/DcfDDggYt8CL6tFRs7qgL/lessons/advanced-react/uncontrolled-forms.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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":"<h2>Uncontrolled Forms</h2>\n<p>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.</p>\n<p>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.</p>\n<pre><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">export</span> <span class=\"hljs-keyword\">default</span> <span class=\"hljs-keyword\">async</span> <span class=\"hljs-keyword\">function</span> <span class=\"hljs-title function_\">postContact</span>(<span class=\"hljs-params\">name, email, message</span>) {\n <span class=\"hljs-keyword\">const</span> response = <span class=\"hljs-keyword\">await</span> <span class=\"hljs-title function_\">fetch</span>(<span class=\"hljs-string\">"/api/contact"</span>, {\n <span class=\"hljs-attr\">method</span>: <span class=\"hljs-string\">"POST"</span>,\n <span class=\"hljs-attr\">headers</span>: {\n <span class=\"hljs-string\">"Content-Type"</span>: <span class=\"hljs-string\">"application/json"</span>,\n },\n <span class=\"hljs-attr\">body</span>: <span class=\"hljs-title class_\">JSON</span>.<span class=\"hljs-title function_\">stringify</span>({ name, email, message }),\n });\n\n <span class=\"hljs-keyword\">if</span> (!response.<span class=\"hljs-property\">ok</span>) {\n <span class=\"hljs-keyword\">throw</span> <span class=\"hljs-keyword\">new</span> <span class=\"hljs-title class_\">Error</span>(<span class=\"hljs-string\">"Network response was not ok"</span>);\n }\n\n <span class=\"hljs-keyword\">return</span> response.<span class=\"hljs-title function_\">json</span>();\n}\n</code></pre><p>Create a new link to our page in index.lazy.jsx</p>\n<pre><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">// after past orders</span>\n<li>\n <span class=\"language-xml\"><span class=\"hljs-tag\"><<span class=\"hljs-name\">Link</span> <span class=\"hljs-attr\">to</span>=<span class=\"hljs-string\">"/contact"</span>></span>Contact<span class=\"hljs-tag\"></<span class=\"hljs-name\">Link</span>></span></span>\n</li>\n</code></pre><p>Create a new route called contact.lazy.jsx in src/routes</p>\n<pre><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">import</span> { createLazyFileRoute } <span class=\"hljs-keyword\">from</span> <span class=\"hljs-string\">"@tanstack/react-router"</span>;\n<span class=\"hljs-keyword\">import</span> { useMutation } <span class=\"hljs-keyword\">from</span> <span class=\"hljs-string\">"@tanstack/react-query"</span>;\n<span class=\"hljs-keyword\">import</span> postContact <span class=\"hljs-keyword\">from</span> <span class=\"hljs-string\">"../api/postContact"</span>;\n\n<span class=\"hljs-keyword\">export</span> <span class=\"hljs-keyword\">const</span> <span class=\"hljs-title class_\">Route</span> = <span class=\"hljs-title function_\">createLazyFileRoute</span>(<span class=\"hljs-string\">"/contact"</span>)({\n <span class=\"hljs-attr\">component</span>: <span class=\"hljs-title class_\">ContactRoute</span>,\n});\n\n<span class=\"hljs-keyword\">function</span> <span class=\"hljs-title function_\">ContactRoute</span>(<span class=\"hljs-params\"></span>) {\n <span class=\"hljs-keyword\">const</span> mutation = <span class=\"hljs-title function_\">useMutation</span>({\n <span class=\"hljs-attr\">mutationFn</span>: <span class=\"hljs-keyword\">function</span> (<span class=\"hljs-params\">e</span>) {\n e.<span class=\"hljs-title function_\">preventDefault</span>();\n <span class=\"hljs-keyword\">const</span> formData = <span class=\"hljs-keyword\">new</span> <span class=\"hljs-title class_\">FormData</span>(e.<span class=\"hljs-property\">target</span>);\n <span class=\"hljs-keyword\">return</span> <span class=\"hljs-title function_\">postContact</span>(\n formData.<span class=\"hljs-title function_\">get</span>(<span class=\"hljs-string\">"name"</span>),\n formData.<span class=\"hljs-title function_\">get</span>(<span class=\"hljs-string\">"email"</span>),\n formData.<span class=\"hljs-title function_\">get</span>(<span class=\"hljs-string\">"message"</span>)\n );\n },\n });\n\n <span class=\"hljs-keyword\">return</span> (\n <span class=\"language-xml\"><span class=\"hljs-tag\"><<span class=\"hljs-name\">div</span> <span class=\"hljs-attr\">className</span>=<span class=\"hljs-string\">"contact"</span>></span>\n <span class=\"hljs-tag\"><<span class=\"hljs-name\">h2</span>></span>Contact<span class=\"hljs-tag\"></<span class=\"hljs-name\">h2</span>></span>\n {mutation.isSuccess ? (\n <span class=\"hljs-tag\"><<span class=\"hljs-name\">h3</span>></span>Submitted!<span class=\"hljs-tag\"></<span class=\"hljs-name\">h3</span>></span>\n ) : (\n <span class=\"hljs-tag\"><<span class=\"hljs-name\">form</span> <span class=\"hljs-attr\">onSubmit</span>=<span class=\"hljs-string\">{mutation.mutate}</span>></span>\n <span class=\"hljs-tag\"><<span class=\"hljs-name\">input</span> <span class=\"hljs-attr\">name</span>=<span class=\"hljs-string\">"name"</span> <span class=\"hljs-attr\">placeholder</span>=<span class=\"hljs-string\">"Name"</span> /></span>\n <span class=\"hljs-tag\"><<span class=\"hljs-name\">input</span> <span class=\"hljs-attr\">type</span>=<span class=\"hljs-string\">"email"</span> <span class=\"hljs-attr\">name</span>=<span class=\"hljs-string\">"email"</span> <span class=\"hljs-attr\">placeholder</span>=<span class=\"hljs-string\">"Email"</span> /></span>\n <span class=\"hljs-tag\"><<span class=\"hljs-name\">textarea</span> <span class=\"hljs-attr\">placeholder</span>=<span class=\"hljs-string\">"Message"</span> <span class=\"hljs-attr\">name</span>=<span class=\"hljs-string\">"message"</span>></span><span class=\"hljs-tag\"></<span class=\"hljs-name\">textarea</span>></span>\n <span class=\"hljs-tag\"><<span class=\"hljs-name\">button</span>></span>Submit<span class=\"hljs-tag\"></<span class=\"hljs-name\">button</span>></span>\n <span class=\"hljs-tag\"></<span class=\"hljs-name\">form</span>></span>\n )}\n <span class=\"hljs-tag\"></<span class=\"hljs-name\">div</span>></span></span>\n );\n}\n</code></pre><ul>\n<li>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.</li>\n<li>Notice we're using <code>isSuccess</code> 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.</li>\n<li>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.</li>\n<li>There's also isError and isIdle, we just didn't use them from TanStack Query.</li>\n<li>Also notice in the dev tools for TanStack Query the mutations (it's in another tab.)</li>\n</ul>\n<p>Try it! You'll notice in the logs of where-ever your API is running that it logs out the contact info.</p>\n<blockquote>\n<p>🏁 <a href=\"https://github.com/btholt/citr-v9-project/tree/master/13-uncontrolled-forms\">Click here to see the state of the project up until now: 13-uncontrolled-forms</a></p>\n</blockquote>\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<li>\n <Link to=\"/contact\">Contact</Link>\n</li>\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 <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\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} |
Oops, something went wrong.