Skip to content

Commit

Permalink
finish 05
Browse files Browse the repository at this point in the history
  • Loading branch information
kentcdodds committed Mar 8, 2024
1 parent c6e32c8 commit e0e3b5b
Show file tree
Hide file tree
Showing 18 changed files with 91 additions and 3 deletions.
28 changes: 28 additions & 0 deletions exercises/05.portals/01.problem.create/README.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# createPortal

👨‍💼 Some of our users find the heart icon to be unclear and would like to have a
tooltip that explains what it's for.

🧝‍♂️ I've moved things around a little bit to reduce the amount of code you need
to work in. I've also added a simple tooltip component that's not working quite
yet. The positioning is all funny because the tooltip is being rendered within
the context of the card instead of at the root of the document.

👨‍💼 Thanks Kellie. Now, let's see if you can make the tooltip component work
properly with a portal.

Note, the change you're making is pretty minimal. You'll also need to change
some of the CSS to make the tooltip look a little better.

🦉 Don't forget about the "Files" button in the bottom of this screen. It will
show you which files are changed in this exercise step and allow you to click
to open the relevant files.

📜 Parts of this exercise was lifted from [the React docs](https://react.dev/reference/react/useLayoutEffect#measuring-layout-before-the-browser-repaints-the-screen)

<callout-info>
Typically you'll want to use a library for tooltips which have been tested for
accessibility best practices. But they're likely using `createPortal` under
the hood and you'll very likely find yourself needing to use this API at some
point.
</callout-info>
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export default function Tooltip({
tooltipY += window.scrollY
}

// 🐨 put this inside a createPortal call and append it to the document.body
return (
<TooltipContainer x={tooltipX} y={tooltipY} contentRef={ref}>
{children}
Expand Down
1 change: 0 additions & 1 deletion exercises/05.portals/01.problem/README.mdx

This file was deleted.

3 changes: 3 additions & 0 deletions exercises/05.portals/01.solution.create/README.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# createPortal

👨‍💼 Great job! That tooltip works much better now. Our users will be thrilled!
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
1 change: 0 additions & 1 deletion exercises/05.portals/01.solution/README.mdx

This file was deleted.

3 changes: 3 additions & 0 deletions exercises/05.portals/FINISHED.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Portals

👨‍💼 Nice and quick on, but useful tool in your React toolbelt!
57 changes: 56 additions & 1 deletion exercises/05.portals/README.mdx
Original file line number Diff line number Diff line change
@@ -1,3 +1,58 @@
# Portals

📜 Parts of this exercise was lifted from [the React docs](https://react.dev/reference/react/useLayoutEffect#measuring-layout-before-the-browser-repaints-the-screen)
There are a variety of UI patterns that require a component render some part of
its UI that appears outside of the component's normal DOM hierarchy. For
example, a modal dialog might need to render its contents at the root of the
document, or a tooltip might need to render its contents at the end of the
`body` element. Typically this is for the purpose of layering or positioning
content above other content.

You could imperatively add a `useEffect` that creates a DOM node yourself,
appends it to the document, and then removes it when the component unmounts.
However, this is a common enough pattern that React provides a built-in way to
do this with the `ReactDOM.createPortal` method.

```tsx lines=1,12-18
import { createPortal } from 'react-dom'

function Modal({
title,
content,
handleClose,
}: {
title: string
content: string
handleClose: () => void
}) {
return createPortal(
<div className="modal">
<h1>{title}</h1>
<p>{content}</p>
</div>,
document.body,
)
}

function App() {
const [showModal, setShowModal] = React.useState(false)

return (
<div>
<button onClick={() => setShowModal(true)}>Show Modal</button>
{showModal && (
<Modal
title="My Modal"
content="This is the content of the modal"
handleClose={() => setShowModal(false)}
/>
)}
</div>
)
}
```

The first argument is the UI you want rendered (which has access to props,
state, whatever) and the second argument is the DOM node you want to render it
to. In this case, we're rendering the modal to the `body` element.

📜 Learn more from [the `createPortal` docs](https://react.dev/reference/react-dom/createPortal)

0 comments on commit e0e3b5b

Please sign in to comment.