0% found this document useful (0 votes)
2 views3 pages

Merge CamScanner 04-02-2025 16.51

The document discusses implementing uncontrolled forms in React for a contact page, utilizing TanStack Query for API mutations. It also covers error boundaries using class components to handle API errors and introduces portals for rendering modals outside the main application structure. Key concepts include managing form state with FormData, error handling in child components, and using portals for modal rendering.

Uploaded by

xlin67948
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views3 pages

Merge CamScanner 04-02-2025 16.51

The document discusses implementing uncontrolled forms in React for a contact page, utilizing TanStack Query for API mutations. It also covers error boundaries using class components to handle API errors and introduces portals for rendering modals outside the main application structure. Key concepts include managing form state with FormData, error handling in child components, and using portals for modal rendering.

Uploaded by

xlin67948
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 3

COMPLETE ADVANCED REACT 

UNCONTROLLED FORMS
INTRO TO REACT

Uncontrolled Forms

We are now going to do a contact page for our


site. As part of that, we will accept a name,
email, and message from our users and
submit it our backend. Like all good backends,
ours will log it to the console and then ignore
it.

We are going to see two new concepts here:


how to do a post with TanStack Query (which
they call a mutation) and how to do
uncontrolled forms with React. Let's start with
our API query. In src/api, create a file called
postContact.js and put this in there.

export default async function postConta


const response = await fetch("/api/co
method: "POST",
headers: {
"Content-Type": "application/json
},
body: JSON.stringify({ name, email,
});

if (!response.ok) {
throw new Error("Network response w
}

return response.json();
}

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

// after past orders


<li>
<Link to="/contact">Contact</Link>
</li>

Create a new route called


contact.lazy.jsx in src/routes

import { createLazyFileRoute } from "@t


import { useMutation } from "@tanstack/
import postContact from "../api/postCon

export const Route = createLazyFileRout


component: ContactRoute,
});

function ContactRoute() {
const mutation = useMutation({
mutationFn: function (e) {
e.preventDefault();
const formData = new FormData(e.t
return postContact(
formData.get("name"),
formData.get("email"),
formData.get("message")
);
},
});

return (
<div className="contact">
<h2>Contact</h2>
{mutation.isSuccess ? (
<h3>Submitted!</h3>
) : (
<form onSubmit={mutation.mutate
<input name="name" placeholde
<input type="email" name="ema
<textarea placeholder="Messag
<button>Submit</button>
</form>
)}
</div>
);
}

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.
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.
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.
There's also isError and isIdle, we just
didn't use them from TanStack Query.
Also notice in the dev tools for TanStack
Query the mutations (it's in another tab.)

Try it! You'll notice in the logs of where-ever


your API is running that it logs out the contact
info.

Click here to see the state of the


project up until now: 13-uncontrolled-
forms

← Previous Next →

Content
Licensed
Under
CC-BY-
NC-4.0
Code
Samples
and
Exercises
Licensed
Under
Apache
2.0
Site
Designed
by Alex
Danielson
COMPLETE INTRO ADVANCED REACT 
ERROR BOUNDARIES
TO REACT

Error Boundaries

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.

This will also catch 404s on our API if someone


give it an invalid ID!

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

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!

A class component is really similar to a


function component (which is what we have
been writing) but have a few key di!erences.

You cannot use any hooks with class


components (useState, useE!ect, etc.)
Every class component has a
render() function. It works mostly the
same as a function component.
Instead of the useState hook you'll use
this.state which is an object that
contains all state for that component.
To change state, you'll use a
this.setState({ key: "value"
}) function. This allows you to change
state like your setKey hook would
have.
Instead of useE!ect, you use lifecycle
methods. componentDidMount,
componentDidUpdate,
componentWillUnmount, etc. To read
more about them, go here.
Instead of a props parameter being
passed into the function, you'll use
this.props .
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.

import { Component } from "react";


import { Link } from "@tanstack/react-r

class ErrorBoundary extends Component {


state = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
componentDidCatch(error, info) {
console.error("ErrorBoundary caught
}
render() {
if (this.state.hasError) {
return (
<div className="error-boundary"
<h2>Uh oh!</h2>
<p>
There was an error with thi
to back to the home page.
</p>
</div>
);
}

return this.props.children;
}
}

export default ErrorBoundary;

Now anything that is a child of this


component will have errors caught here.
Think of this like a catch block from
try/catch.
A static method is one that can be called
on the constructor. You'd call this
method like this:
ErrorBoundary.getDerivedStateFromEr
This method must be static.
If you want to call an error logging
service, componentDidCatch would
be an amazing place to do that. I can
recommend Sentry and TrackJS.
this.props.children is any child
component inside of the component. e.g.
if someone renders <MyComponent>
<h1>hi</h1></MyComponent> , the
<h1>hi</h1> is consider the the
"children". This works in function
components too.
Because we just return children if there's
no error, the ErrorBoundary component
doesn't render anything itself if there are
no errors.

Let's go make PastOrderRoute use it in case


the API throws an error.

// add import
import ErrorBoundary from "../ErrorBoun

// replace Route export


export const Route = createLazyFileRout
component: ErrorBoundaryWrappedPastOr
});

// beneath Route export


function ErrorBoundaryWrappedPastOrderR
return (
<ErrorBoundary>
<PastOrdersRoute />
</ErrorBoundary>
);
}

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. Di!ering
opinions exist.
We totally could have made
ErrorBoundary a bit more flexible and
made it able to accept a component to
COMPLETE INTRO TO ADVANCED REACT 
PORTALS
REACT

Portals

What if you are rendering a page and you want


to render something in another part of the
page at the same time? Think like you have a
contextual right navigation and you have a
page that wants to render into the contextual
nav. We could use something like context to
do that and that would be totally acceptable.
But there's another cool way called a portal to
do this as well. It even lets us render outside
our app totally, like we're about to do.

We're going to do a modal or a "popover". I


think these are bad user experiences but it's a
perfect use case for a portal so we're going to
do it! In our case, because we want the modal
to render in front of everything, we need the
div that the modal renders into to be first in
the DOM. So let's go make a div that does that.
Open your index.html file and put this in there.

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

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

// basically stolen from the React docs


import { useEffect, useRef } from "reac
import { createPortal } from "react-dom

const Modal = ({ children }) => {


const elRef = useRef(null);
if (!elRef.current) {
elRef.current = document.createElem
}

useEffect(() => {
const modalRoot = document.getEleme
modalRoot.appendChild(elRef.current
return () => modalRoot.removeChild(
}, []);

return createPortal(<div>{children}</
};

export default Modal;

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.
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.
We use the returned function on the
e!ect to clean up when this Modal is
unmounted from the DOM. Otherwise
we'd leak memory.

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.

export default async function getPastOr


const response = await fetch(`/api/pa
const data = await response.json();
return data;
}

Then render the modal if we have a


focusedOrder :

// import at top
import getPastOrder from "../api/getPas
import Modal from "../Modal";

// NOTE: In the course, Brian makes thi


const intl = new Intl.NumberFormat("en-
style: "currency",
currency: "USD",
});

// top of the render function


const [focusedOrder, setFocusedOrder] =

const { isLoading: isLoadingPastOrder,


queryKey: ["past-order", focusedOrder
queryFn: () => getPastOrder(focusedOr
enabled: !!focusedOrder,
staleTime: 24 * 60 * 60 * 1000, // on
});

// last thing before closing div


{
focusedOrder ? (
<Modal>
<h2>Order #{focusedOrder}</h2>
{!isLoadingPastOrder ? (
<table>
<thead>
<tr>
<td>Image</td>
<td>Name</td>
<td>Size</td>
<td>Quantity</td>
<td>Price</td>
<td>Total</td>
</tr>
</thead>
<tbody>
{pastOrderData.orderItems.m
<tr key={`${pizza.pizzaTy
<td>
<img src={pizza.image
</td>
<td>{pizza.name}</td>
<td>{pizza.size}</td>
<td>{pizza.quantity}</t
<td>{intl.format(pizza.
<td>{intl.format(pizza.
</tr>
))}
</tbody>
</table>
) : (
<p>Loading …</p>
)}
<button onClick={() => setFocused
</Modal>
) : null
}

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 .
We set the staleTime to day. These
orders shouldn't change very frequently.
If there's no focusedOrder, we don't
render the Modal, which means the
portal is unused.
If there is a focusedOrder, we render the
modal and show a loading indicator that
we're loading the rest of the info.
When a user clicks Close, we set the
focusedOrder to be undefined again
which causes the Modal to unrender.

Finally, we need a way to open the modal.


Open past.lazy.jsx . Make the
order.order_id value a button and add
an onClick event so clicking it will open the
Modal:

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

That's it!

Click here to see the state of the


project up until now: 11-modals

← Previous Next →

Content
Licensed
Under
CC-BY-
NC-4.0
Code
Samples
and
Exercises
Licensed
Under
Apache
2.0
Site
Designed
by Alex
Danielson

You might also like