1
votes

I am creating one Alert component. For styling I am using Style-components. For typescript I put props any. My alert component works fine. And it Looks like this. As soon as I add interface props I am getting bunch of errors. first error is Cannot invoke an object which is possibly 'undefined'.

This is the parent component where I am using my Alert component.

 <button
        style={{ color: "red" }}
        onClick={() =>
          Alert({
            id: "sign-in-fail-alert",
            title: "Good Job!",
            alertType: "success",
            onConfirm: () => setValue("click"),
            children: <div>You clicked the button! </div>
          })
        }
      >
        Show alert!
      </button>

This is my Alert component. Inside the Alert component I created the Modal then I passed it to the Alert variable. My alert variable's interface works fine. I am getting error from my Modal interface.

import React from "react";
import ReactDOM from "react-dom";
import styled from "styled-components";
import { Button } from "components/buttons";
import { CloseIcon } from "assets/icons";
export const Container = styled.div`
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: #000000e0;
  position: fixed;
  z-index: 3;
  display: flex;
  flexdirection: row;
  justifycontent: center;
  alignitems: center;
  aligncontent: center;
`;
export const ModalOverlay = styled.div`
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.8);
  cursor: pointer;
  z-index: 1;
`;
export const ModalClose = styled.button`
  position: absolute;
  top: 20px;
  right: 20px;
  transition: transform 250ms ease-in-out;
  transform-origin: 50% 50%;
  &:hover {
    transform: rotate(180deg);
  }
`;
export const IconContainer = styled.div`
  position: relative;
  z-index: 2;
  top: 6%;
  left: 47%;
  box-sizing: content-box;
  border-radius: 50%;
`;
export const ModalBox = styled.div`
  position: relative;
  width: 40%;
  padding: 1.25em;
  border: none;
  margin: 10% 10% 10% 25%;
  box-sizing: border-box;
  border-radius: 10px;
  background-color: white;
  cursor: auto;
  z-index: 2;
`;
export const Confirm = styled(Button)`
  position: relative;
  top: 100%;
  left: 50%;
`;
export const Title = styled.h1`
  position: relative;
  top: 40%;
  font-size: 50px;
  text-align: center;
  color: ${({ theme }) => theme.textColor};
`;

export const Children = styled.h1`
  position: relative;
  top: 40%;
  font-size: 20px;
  text-align: center;
  color: ${({ theme }) => theme.textColor};
`;

export interface IModal {
  title: string;
  onClose?: () => void;
  onCancel?: () => void;
  onConfirm?: () => void;
  children?: JSX.Element | string;
  icon?: JSX.Element;
  alertType?: "success" | "error" | "warning" | "info" | "question";
}
export const Modal = ({
  title,
  children,
  onClose,
  onCancel,
  onConfirm,
  icon,
  alertType
}:  IModal) => { // If I type any it works fine
  const handleCancel = () => {
    onCancel(); //Getting error from here
    onClose(); //Getting error from here
  };
  const handleConfirm = () => {
    onConfirm(); //Getting error from here
    onClose(); //Getting error from here
  };
  return (
    <Container>
      <ModalOverlay onClick={onClose} />
      <ModalBox>
        <ModalClose onClick={onClose}>
          <CloseIcon />
        </ModalClose>
        <Title>{title}</Title>
        <IconContainer>
          {icon ? (
            icon
          ) : alertType === "success" ? (
            <CloseIcon />
          ) : alertType === "error" ? (
            <p>error</p>
          ) : alertType === "warning" ? (
            <p>warning</p>
          ) : alertType === "info" ? (
            <p>info</p>
          ) : alertType === "question" ? (
            <p>question</p>
          ) : null}
        </IconContainer>
        <Children> {children}</Children>
        {onCancel && <Button onClick={handleCancel}>Cancel</Button>}
        {onConfirm && <Confirm onClick={handleConfirm}>Confirm</Confirm>}
      </ModalBox>
    </Container>
  );
};

export interface IAlert {
  title: string;
  children?: JSX.Element | string;
  id: string;
  alertType: string;
  onCancel?: () => void;
  onConfirm?: () => void;
}


export const Alert = ({
  title,
  children,
  id,
  alertType,
  onCancel,
  onConfirm
}: IAlert) => {
  // search the DOM for an element with the same ID and remove it
  const removeElement = () => document.getElementById(id)?.remove();
  const createElement = () => {
    const container = document.createElement(`div`);
    container.setAttribute(`class`, "alert");
    container.setAttribute(`id`, id);
    document.body.appendChild(container);
    ReactDOM.render(
      <Modal
        title={title}
        alertType={alertType}
        onCancel={onCancel}
        onConfirm={onConfirm}
        onClose={removeElement}
      >
        {children}
      </Modal>,
      container
    );
  };
  return createElement();
};
1
Those properties are optional, which means they might be undefined. - Roberto Zvjerković
I want to use properties as an optional. So I can use based on my alert situation. How can I keep them as optional and overcome the error? - Krisna
if (onCancel) { onCancel(); } - Roberto Zvjerković
Thank you ritaj. Although I used onCancel && onCancel();. But used your logic. 👏🏾 - Krisna
alertType?: "success" | "error" | "warning" | "info" | "question"; and alertType: string; are not matching. - Roberto Zvjerković

1 Answers

0
votes

You defined properties on your type that are optional, which is why you get the compiler messages. If you would remove the ? behind the onCancel and onConfirm properties, they would no longer be optional, and you wouldn't get the error.

export interface IAlert {
  title: string;
  children?: JSX.Element | string;
  id: string;
  alertType: string;
  onCancel: () => void;
  onConfirm: () => void;
}

If you can change the interface, you would at least have to validate that they exist before calling them, for example by:

const { onConfirm } = this.props;
onConfirm && onConfirm();

One thing to note, the children properties are not necessary, they are standard for a React component, so no need to define those