Skip to content

Exercise

ToastShelf portal

In the 2nd project, we built a generic Toast component from scratch:

In addition to the Toast component, we also created a ToastShelf component, which was responsible for rendering a list of toasts in the bottom-right corner of the page.

Let's use a portal to ensure that the ToastShelf component always renders without issue!

Acceptance Criteria:

  • The ToastShelf component should use the createPortal function from React DOM to render its contents (the <ol>) in a different root node.
    • You'll need to add this new root to the index.html file.

Code Playground

import React from 'react';

import { ToastContext } from '../ToastProvider';
import Toast from '../Toast';
import styles from './ToastShelf.module.css';

function ToastShelf() {
const { toasts } = React.useContext(ToastContext);

return (
<ol
className={styles.wrapper}
role="region"
aria-live="assertive"
aria-label="Notification"
>
{toasts.map((toast) => (
<li key={toast.id} className={styles.toastWrapper}>
<Toast id={toast.id} variant={toast.variant}>
{toast.message}
</Toast>
</li>
))}
</ol>
);
}

export default ToastShelf;

Solution:

A Portal Component

Alright, let's do something fun and/or challenging: let's create a reusable Portal component!

This time, instead of editing the HTML to include a <div id="toast-root">, our Portal component will create this node dynamically. When the component mounts, it should create a brand-new DOM node, and use it as the portal's root node.

This is a challenging problem, and I don't expect you to be able to solve it. This is one of those things where we can learn a heck of a lot by fiddling and experimenting with different ideas. Give it a shot, and then watch the solution video for a breakdown.

Acceptance Criteria:

  • The Portal component should use the createPortal function from React DOM to render its children in a portal.
  • When the component is unmounted, it should clean up after itself; any dynamically-created nodes should be destroyed.
  • You'll know it's working when the inner box is teleported outside its parent:
Two boxes sitting adjacent, side-by-side

Code Playground

import React from 'react';

function Portal({ children }) {
return children;
}

export default Portal;

Solution: