skip to Main Content

I’ve loosely followed this react modal tutorial.
When I put the Modal inside a loop with multiple links and open the modal, It’s opening all the modals (of course). I’m trying to work out how to open the appropriate modal (for the id)

The Alert component with Modal in loop

export default function Alert({}: Props) {
  const { alerts, loading } = useGetAlerts();
  const { isShown, toggle } = useModal();

  return (
    <div className={styles.alertHeader}>
      <div className={styles.title}>Alerts</div>
      <div className={styles.alertFrame}>
        <div className={styles.counter}>{`${alerts?.items.length}`}</div>
        <div className={styles.titleFrame}>
          <div className={styles.summaryTitle}>
            Outstanding <br></br>Alerts
          </div>
        </div>
      </div>
      {loading ? (
        <SnakeLoader />
      ) : (
        alerts?.items.slice(0, 5).map((a) => (
          <div key={`id_${a}`} className={styles.alertFrame}>
            <div className={styles.titleFrame} id="titleFrame">
              <div className={styles.alertTitle} id="alertTitle">
                {' '}
                {`${a.message}`}{' '}
              </div>
              <div className={styles.alertLink} id="alertLink">
                <button onClick={toggle}>Click for More</button>
                <Modal
                  isShown={isShown}
                  hide={toggle}
                  modalContent={a.message}
                  headerText={a.id}
                />
              </div>
            </div>
            <div className={styles.alertClose} id="alertClose">
              X
            </div>
          </div>
        ))
      )}
    </div>
  );
}

The Modal component

import React, { FunctionComponent, useEffect } from 'react';
import ReactDOM from 'react-dom';
import styles from './Modal.module.scss';
//https://nainacodes.com/blog/create-an-accessible-and-reusable-react-modal

export interface ModalProps {
  isShown: boolean;
  hide: () => void;
  modalContent: string;
  headerText: string;
}

export const Modal: FunctionComponent<ModalProps> = ({
  isShown,
  hide,
  modalContent,
  headerText,
}) => {
  const modal = (
    <div className={styles.modalContainer}>
      <div className={styles.styledModal}>
        <div className={styles.header}>
          <div className={styles.headerText}>{headerText}</div>
          <button className={styles.closeButton} onClick={hide}>
            X
          </button>
        </div>
        <div className={styles.content}>{modalContent}</div>
      </div>
    </div>
  );

  return isShown ? ReactDOM.createPortal(modal, document.body) : null;
};

The useModal method

export const useModal = () => {
  const [isShown, setIsShown] = useState<boolean>(false);
  const toggle = () => setIsShown(!isShown);
  const id = '';
  return {
    isShown,
    toggle,
    id
  };
};

Very new to React, any help greatly appreciated

2

Answers


  1. Currently each of your modals use the same piece of state to toggle its visibility, isShown. And toggle is the function that changes that state. In order to control the modals individually, we need a separate piece of state and a separate function to toggle that state for each individual modal you have created. One option could be getting rid of the useModal hook, and moving both isShown and toggle into the Modal component itself. This way when each Modal component is created, it has its own state instead of sharing it from that hook.

    Login or Signup to reply.
  2. Basically there is no need to render that much Modal components, render only one of it.

    Add 1 extra state variable that will hold the activeAlertItem for modal. This variable will be set by Click for More button click for specified alert.

    Once set – you are free to use either conditional rendering ie

    {activeAlertItem && (
      <Modal
        isShown={true}
        hide={() => setActiveAlertItem(undefined)}
        modalContent={activeAlertItem.message}
        headerText={activeAlertItem.id}
      />
    )}
    

    either whatever else you prefer.

    Here is an example (with manual dummy data, sorry):

    interface IAlertItem {
      id: string;
      message: string;
    }
    
    const alertsItems: IAlertItem[] = [
      { id: "1", message: "test 1" },
      { id: "2", message: "test 2" },
      { id: "3", message: "test 3" }
    ];
    const alerts = {
      items: alertsItems
    };
    
    export default () => {
      const [loading, setLoading] = useState(false);
      const [activeAlertItem, setActiveAlertItem] = useState<IAlertItem | undefined>(undefined);
    
      return (
        <div className={styles.alertHeader}>
          <div className={styles.title}>Alerts</div>
          <div className={styles.alertFrame}>
            <div className={styles.counter}>{`${alerts?.items.length}`}</div>
            <div className={styles.titleFrame}>
              <div className={styles.summaryTitle}>
                Outstanding <br></br>Alerts
              </div>
            </div>
          </div>
          {loading ? (
            <SnakeLoader />
          ) : (
            alerts?.items.slice(0, 5).map((a) => (
              <div key={`id_${a}`} className={styles.alertFrame}>
                <div className={styles.titleFrame} id="titleFrame">
                  <div className={styles.alertTitle} id="alertTitle">
                    {" "}
                    {`${a.message}`}{" "}
                  </div>
                  <div className={styles.alertLink} id="alertLink">
                    <button onClick={() => setActiveAlertItem(a)}>
                      Click for More
                    </button>
                  </div>
                </div>
                <div className={styles.alertClose} id="alertClose">
                  X
                </div>
              </div>
            ))
          )}
    
          {activeAlertItem && (
            <Modal
              isShown={true}
              hide={() => setActiveAlertItem(undefined)}
              modalContent={activeAlertItem.message}
              headerText={activeAlertItem.id}
            />
          )}
        </div>
      );
    };
    

    Edit thirsty-tereshkova-g6zrk9

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search