
Creating the `ToastList` component in React
Creating the `ToastList` component in React 관련
Let’s define the list component now and add some props. The ToastList component takes in three props: data, position, and removeToast. The data prop represents an array that will contain objects, position determines the placement of the toast list on the page, and removeToast acts as a callback to be provided to the onClose attribute of the Toast component:
import React from "react";
import Toast from "../Toast/Toast";
import "./ToastList.css";
const ToastList = ({ data, position, removeToast }) => {
  ...
};
export default ToastList;
To use the position prop, add it to the element with a className of toast-list as shown below:
const ToastList = ({ data, position, removeToast }) => {
  return (
    <div
      className={`toast-list toast-list--${position}`}
      aria-live="assertive"
    >
      ..
    </div>
  );
};
Whatever position prop is passed into the toast component, it will be added as a class to those elements (recall that we already set the CSS position properties in the CSS file). Because data is an array, we can loop through it directly in the JSX.
First, import the useRef and useEffect Hooks from React where the useRef will be used to get the reference of the toast list without relying on traditional Web API methods in JavaScript. The useEffect Hook will be called when re-rendering is required:
import React, { useRef, useEffect } from 'react';
Add this after the props destructuring:
const listRef = useRef(null);
As already discussed, the structure of our ToastList is a simple wrapper element with Toast components as its contents. The position prop determines the dynamic CSS classes, which dictate the position of the ToastList on the screen.
Within its contents, we can iterate over the data prop and include a Toast component for each item in data, ensuring that the appropriate props are passed. For instance, we will assign the removeToast callback to the onClose prop of each Toast component and decide the toast-removal logic later on in the app component:
const ToastList = ({ data, position, removeToast }) => {
  return (
    data.length > 0 && (
      <div
        className={`toast-list toast-list--${position}`}
        aria-live="assertive"
      >
        {data.map((toast) => (
          <Toast
            key={toast.id}
            message={toast.message}
            type={toast.type}
            onClose={() => removeToast(toast.id)}
          />
        ))}
      </div>
    )
  );
};
Before proceeding, it’s important to consider the correct ordering and scrolling of toast notifications based on the position of ToastList. While it may appear sufficient at first glance, there are some crucial aspects to address.
By default, toast notifications are added to the list from top to bottom. Each new notification is placed at the bottom, and if the list exceeds the maximum height limit, a scrollbar appears. However, the scrollbar currently doesn’t adjust and jumps to the latest notification. This can be improved for a better user experience.
Moreover, when the ToastList is positioned at the bottom-left or bottom-right, the flow of toast notifications should be reversed. In other words, the most recent toast should be displayed at the top rather than the bottom. This simple adjustment is crucial for creating a position-intuitive toast list that enhances the overall user experience.
To fix the scrolling issue, we can use the Element.scrollTo method from the JavaScript Web API. Additionally, we will use the useRef and useEffect Hooks from the React library. This will allow us to obtain a reference to the toast list without relying on Document.getElementById, and enable us to adjust the scroll whenever there are changes to the position or data props:
import React, { useEffect, useRef } from "react";
const ToastList = ({ data, position, removeToast }) => {
  const listRef = useRef(null);
  const handleScrolling = (el) => {
    const isTopPosition = ["top-left", "top-right"].includes(position);
    if (isTopPosition) {
      el?.scrollTo(0, el.scrollHeight);
    } else {
      el?.scrollTo(0, 0);
    }
  };
  useEffect(() => {
    handleScrolling(listRef.current);
  }, [position, data]);
  ...
};
Additionally, the data reversal process can be simplified by using the spread operator on the data array and subsequently applying the reverse() method. These steps should be performed after checking if the current position of the ToastList is either bottom-left or bottom-right:
const ToastList = ({ data, position, removeToast }) => {
  // ...
  const sortedData = position.includes("bottom")
    ? [...data].reverse()
    : [...data];
  return (
    sortedData.length > 0 && (
      <div
        className={`toast-list toast-list--${position}`}
        aria-live="assertive"
        ref={listRef}
      >
        {sortedData.map((toast) => (
          <Toast
            ...
          />
        ))}
      </div>
    )
  );
};  
This concludes our ToastList component. You can view its full code here (c99rahul/react-toast). If you are wondering what kind of data would be passed to ToastList, here’s the structure of the object array that will be provided to the data prop:
[
  {
    id: 1,
    message: "This is a success toast component",
    type: "success"
  }, {
    id: 2,
    message: "This is a failure toast message.",
    type: "failure"
  }, {
    id: 3,
    message: "This is a warning toast message.",
    type: "warning",
  }
];