组件的设计与实现
实现 ModalContext
关于 Context 的概念和具体使用,可以阅读 React Context 官方文档。
首先,根据可能需要自定义的属性,构建 ModalContext
对象:
// Context.tsx import { createContext, useContext } from 'react' import type { ButtonProps, ModalProps as _ModalProps } from '@nextui-org/react' export type ModalFooterParams = { ConfirmButton: (props: ButtonProps) => React.ReactNode CancelButton: (props: ButtonProps) => React.ReactNode onClose: () => void } export type ModalFooterRenderer = (params: ModalFooterParams) => React.ReactNode export type ModalType = 'default' | 'primary' | 'success' | 'warning' | 'danger' export type ModalProps = { /** 模态框类型 */ type?: ModalType /** 模态框标题 */ title?: React.ReactNode /** 模态框内容 */ content?: React.ReactNode /** 自定义底部内容 */ footer?: ModalFooterRenderer | React.ReactNode /** 关闭回调 */ onClose?: () => void /** 确认按钮文本 */ confirmButtonText?: string /** 确认按钮属性 */ confirmButtonProps?: ButtonProps /** 确认按钮图标 */ confirmButtonIcon?: React.ReactNode /** 确认回调 */ onConfirm?: () => void /** 确认前的校验回调 */ beforeConfirm?: () => boolean /** 确认按钮加载状态 */ isConfirmLoading?: boolean /** 取消按钮文本 */ cancelButtonText?: string /** 取消按钮属性 */ cancelButtonProps?: ButtonProps /** 取消按钮图标 */ cancelButtonIcon?: React.ReactNode /** 取消回调 */ onCancel?: () => void } & Partial<Omit<_ModalProps, 'title' | 'content'>> export const ModalContext = createContext<ModalProps>({} as ModalProps) export const useModalContext = () => { return useContext(ModalContext) }
实现 ModalFooter 组件
首先,定义按钮的基本属性以及与之对应的颜色映射关系。
随后,分别对 ConfirmButton
、CancelButton
以及 ModalFooter
组件展开具体的实现,各组件内根据获取到的上下文属性及相关逻辑来处理按钮的显示、点击等行为。具体代码如下:
// Footer.tsx import { useState } from 'react' import { Button, ButtonProps, ModalFooter as _ModalFooter, useModalContext as _useModalContext, } from '@nextui-org/react' import { useModalContext } from './Context' export const COLOR_MAP = { default: 'bg-black dark:text-black dark:bg-white', primary: 'bg-secondary', success: 'bg-success', warning: 'bg-warning', danger: 'bg-red-500', } export function ConfirmButton(props: ButtonProps) { const { onClose: _onClose } = _useModalContext() const { type = 'default', confirmButtonText = '确认', confirmButtonProps, confirmButtonIcon, onClose, onConfirm, beforeConfirm, isConfirmLoading, } = useModalContext() const [isLoading, setIsLoading] = useState(false) const onClick = async () => { if (confirmButtonProps?.type === 'submit') { return } setIsLoading(!!isConfirmLoading) try { const isConfirm = await beforeConfirm?.() if (isConfirm !== false) { await onConfirm?.() onClose?.() _onClose() } } finally { setIsLoading(false) } } return ( <Button color={type} className={`w-max py-2 px-8 rounded-lg flex items-center gap-2 text-white border-slate-400 ${COLOR_MAP[type]}`} startContent={confirmButtonIcon} isLoading={isLoading} onClick={onClick} {...props} {...confirmButtonProps}> {confirmButtonText} </Button> ) } export function CancelButton(props: ButtonProps) { const { onClose: _onClose } = _useModalContext() const { cancelButtonText = '取消', cancelButtonProps, cancelButtonIcon, onCancel, onClose, } = useModalContext() const onClick = () => { onCancel?.() onClose?.() _onClose() } return ( <Button className='rounded-lg text-inherit bg-transparent border border-black/40 dark:border-white hover:bg-default-100' startContent={cancelButtonIcon} onClick={onClick} {...props} {...cancelButtonProps}> {cancelButtonText} </Button> ) } export function ModalFooter() { const { onClose } = _useModalContext() const { footer } = useModalContext() const defaultFooter = ( <div className='flex items-center gap-2'> <CancelButton /> <ConfirmButton /> </div> ) if (typeof footer === 'function') { return <_ModalFooter>{footer({ ConfirmButton, CancelButton, onClose })}</_ModalFooter> } return footer !== null ? ( <_ModalFooter>{footer || defaultFooter}</_ModalFooter> ) : null }
实现 Modal 组件
Modal
组件接收一系列属性,并通过 ModalContext.Provider
将这些属性传递给子组件。另外,Modal
组件对弹窗的关闭逻辑进行了整合;当我们关闭模态框时,会依次触发 onCancel
和 onClose
回调函数,确保关闭流程的可控性。具体代码如下:
// Modal.tsx import { Modal as _Modal, ModalBody, ModalHeader, ModalContent, ModalFooter as _ModalFooter, useModalContext as _useModalContext, } from '@nextui-org/react' import { ModalContext, type ModalProps } from './Context' import { ModalFooter } from './Footer' import { withType } from './modal' export function Modal(props: ModalProps) { const { title, content, footer, onClose: _onClose, children, confirmButtonText, confirmButtonProps, onConfirm, beforeConfirm, isConfirmLoading, cancelButtonText, cancelButtonProps, onCancel, ...restProps } = props function onClose() { onCancel?.() _onClose?.() } return ( <ModalContext.Provider value={props}> <_Modal disableAnimation className='m-auto' onClose={onClose} {...restProps}> <ModalContent> <ModalHeader>{title}</ModalHeader> <ModalBody className='text-sm'> {content || children} </ModalBody> <ModalFooter /> </ModalContent> </_Modal> </ModalContext.Provider> ) } Modal.default = withType('default') Modal.primary = withType('primary') Modal.success = withType('success') Modal.warning = withType('warning') Modal.danger = withType('danger')
在上述代码中,通过向 withType
函数传入特定的 type
参数来创建不同类型的 Modal
组件。
接下来,对 Modal
组件的渲染逻辑以及创建不同类型 Modal
组件的方法进行封装,相关代码如下:
// modal.tsx import { isValidElement } from 'react' import { createRoot } from 'react-dom/client' import { Modal } from './Modal' import type { ModalProps, ModalType } from './Context' function modal(config: ModalProps) { const currentConfig = { ...config, isOpen: true, onClose } const container = document.createDocumentFragment() const root = createRoot(container) function render(config: ModalProps) { root.render(<Modal {...config} />) } function onClose() { render({ ...currentConfig, isOpen: false, }) setTimeout(function () { root.unmount() }, 300) } render(currentConfig) return { onClose, } } export function withType(type: ModalType) { function _withType( content: React.ReactNode | ModalProps, config?: Omit<ModalProps, 'content'> ): Promise<void> { const _config = isValidElement(content) || typeof content === 'string' ? { ...config, content } : content as ModalProps return new Promise((resolve, reject) => { const onConfirm = async () => { await _config.onConfirm?.() resolve() } const onCancel = async () => { await _config.onCancel?.() reject() } modal({ ..._config, onConfirm, onCancel, type, }) }) } return _withType }
组件的使用方式
该组件提供了三种预期使用方式。
Modal.success("操作成功!"); Modal.success({ title: "提示", content: "操作成功!", confirmButtonText: "好的", }); Modal.success("操作成功!", { title: "提示", confirmButtonText: "好的", });
传入配置对象
定义 handleClick
函数,随后在函数内部调用 Modal
组件,并传入配置对象:
import { type ModalType } from '@/components/Modal/Context' import { COLOR_MAP } from '@/components/Modal/Footer' import { Modal } from '@/components/Modal' import { Button } from '@nextui-org/react' export default function Home() { const colorList: ModalType[] = ['default', 'primary', 'success', 'warning', 'danger'] const handleClick = (type: ModalType) => { Modal[type]({ title: '提示', content: `${type} 操作成功!`, confirmButtonText: '好的', cancelButtonText: '取消', }) } return ( <div className='py-12 flex gap-2 items-center justify-center'> {colorList.map((color) => ( <Button key={color} color={color} className={`rounded-lg text-white ${COLOR_MAP[color]}`} onClick={() => handleClick(color)} > {color} Click </Button> ))} </div> ) }
效果分别如下:
传入文本内容
对 handleClick
函数中 Modal
组件的调用形式进行修改,并且只传入文本内容:
const handleClick = (type: ModalType) => { Modal[type](`${type} 操作成功!`) }
效果分别如下:
传入文本内容和配置对象
修改 handleClick
函数中 Modal
组件的调用形式,同时传入文本内容与配置对象:
const handleClick = (type: ModalType) => { Modal[type](`${type} 操作成功!`, { title: '提示', confirmButtonText: '好的', cancelButtonText: '取消', }) }
效果分别如下:
至此,调用式 Modal
组件的实现流程已全部完成。
最后
以上就是基于React实现调用式Modal组件的全流程的详细内容,更多关于React调用式Modal组件的资料请关注脚本之家其它相关文章!
来源链接:https://www.jb51.net/javascript/332400iof.htm
如有侵犯您的版权,请及时联系3500663466#qq.com(#换@),我们将第一时间删除本站数据。
暂无评论内容