-4

Tạo modal với Nextjs 14

  1. Tạo context API: xử lý kèm logic:
"use client";
import { $ModalKeys } from "@/collects/modals";
import {
  Dispatch,
  PropsWithChildren,
  SetStateAction,
  createContext,
  useState,
} from "react";

type $ToggleFn = { key: $ModalKeys; data?: any };

type $Show = {
  [T in $ModalKeys]: {
    open: boolean;
    data?: any;
  };
};

type $_Modal = {
  onToggle: (props: $ToggleFn) => void;
  show: $Show;
  setShow: Dispatch<SetStateAction<$Show>>;
};

export let _$modal = {} as $_Modal;

const useLogic = () => {
  const [show, setShow] = useState({} as $Show);

  const onToggle = (props: $ToggleFn) => {
    const { key, data } = props;
    setShow((prev) => ({
      ...prev,
      [key]: { ...prev[key], open: !prev[key]?.open, data },
    }));
  };

  _$modal = {
    onToggle,
    show,
    setShow,
  };

  return { onToggle, show, setShow };
};

type ValueCtx = ReturnType<typeof useLogic>;

export const ModalCtx = createContext({} as ValueCtx);

export const ModalProvider = ({ children }: PropsWithChildren) => {
  const valueCtx = useLogic();
  return (
    <ModalCtx.Provider value={{ ...valueCtx }}>
      <>{children}</>
    </ModalCtx.Provider>
  );
};

  1. Tạo Component Modal :
"use client";
import cx from "classnames";
import React, { useContext } from "react";
import ReactDOM from "react-dom";
import styles from "./styles.module.scss";
import { ModalCtx } from "./context";

export type $Modal = {
  open?: boolean;
  onToggle: () => void;
  data?: any;
};

type $Props = {
  set?: {
    open?: $Modal["open"];
    isCloseOutside?: boolean;
    clsOverlay?: string;
    clsModal?: string;
    content?: (props: $Modal) => JSX.Element;
    header?: (props: $Modal) => JSX.Element;
  };
  action: {
    onToggle: () => void;
  };
};

export default function Modal(props: $Props) {
  const { set, action } = props;
  const {} = useContext(ModalCtx);

  if (!set?.open) {
    return null;
  }

  return ReactDOM.createPortal(
    <div
      className={cx(styles.overlay, set?.clsOverlay)}
      onClick={set?.isCloseOutside ? action?.onToggle : undefined}
    >
      <div className={cx(styles.modal, set?.clsModal)} onClick={onStopPrpg}>
        {set?.header?.({ onToggle: action.onToggle })}
        {set.content?.({ onToggle: action.onToggle })}
      </div>
    </div>,
    document.body
  );
}

const onStopPrpg = (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
  event.stopPropagation();
  event.preventDefault();
};

  1. Tạo SCSS : file styles.module.scss
.overlay {
  position: fixed;
  width: 100vw;
  height: 100vh;
  top: 0;
  display: flex;
  align-items: center;
  justify-content: center;

  .modal {
    background-color: lightblue;
    display: flex;
    flex-direction: column;
    width: fit-content;
    border-radius: 0.5rem;
    padding: 1rem;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);

    .header {
      display: flex;
    }
  }
}

Đơn giản đúng hông, có thể tuỳ chỉnh styles tuỳ thích. Phần tiếp theo là Dropbox


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí