You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何以更符合DRY原则编写代码?React+TypeScript下Modals重构及类型问询

Hey there! Let's dive into your two questions—refactoring Modals to follow the DRY principle in React + TypeScript, and clarifying the types for React elements. Here's a breakdown tailored to your stack:

DRY原则下的React Modal重构

Modals are a common pain point for repeated code, so here are actionable steps to refactor them cleanly:

1. 封装通用基础Modal组件

First, extract all shared Modal logic (like open/close state handling, overlay, animations, header/footer structure) into a reusable base component. Define TypeScript props to handle dynamic parts:

import React from 'react';

interface BaseModalProps {
  isOpen: boolean;
  onClose: () => void;
  title?: string;
  footer?: React.ReactNode;
  children: React.ReactNode; // 可变的Modal内容
  className?: string;
}

const BaseModal: React.FC<BaseModalProps> = ({
  isOpen,
  onClose,
  title,
  footer,
  children,
  className,
}) => {
  if (!isOpen) return null;

  return (
    <div className={`modal-overlay ${className}`} onClick={onClose}>
      <div className="modal-content" onClick={(e) => e.stopPropagation()}>
        {title && <div className="modal-title">{title}</div>}
        <button className="modal-close-btn" onClick={onClose}>×</button>
        <div className="modal-body">{children}</div>
        {footer && <div className="modal-footer">{footer}</div>}
      </div>
    </div>
  );
};

export default BaseModal;

Now you can reuse this component across your app by passing in unique content, titles, or footers instead of rewriting the entire Modal structure every time.

2. 用自定义Hook集中管理Modal状态

Avoid duplicating isOpen state and handleClose functions in every component that uses a Modal. Create a custom hook to handle state logic once:

import { useState } from 'react';

const useModal = () => {
  const [isOpen, setIsOpen] = useState(false);

  const showModal = () => setIsOpen(true);
  const hideModal = () => setIsOpen(false);

  return {
    isOpen,
    showModal,
    hideModal,
  };
};

export default useModal;

Usage in a component:

import BaseModal from './BaseModal';
import useModal from './useModal';

const UserProfileModal = () => {
  const { isOpen, showModal, hideModal } = useModal();

  return (
    <>
      <button onClick={showModal}>Edit Profile</button>
      <BaseModal
        isOpen={isOpen}
        onClose={hideModal}
        title="Edit Your Profile"
        footer={<button onClick={hideModal}>Save Changes</button>}
      >
        {/* 这里放你的表单或内容 */}
        <input type="text" placeholder="Name" />
        <input type="email" placeholder="Email" />
      </BaseModal>
    </>
  );
};

3. 复用业务逻辑(如果适用)

If multiple Modals share business logic (like form submission, API calls, or validation), extract that into another custom hook. For example:

import { useState } from 'react';

const useModalForm = <T>(initialValues: T, onSubmit: (values: T) => void) => {
  const [formValues, setFormValues] = useState(initialValues);

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value } = e.target;
    setFormValues(prev => ({ ...prev, [name]: value }));
  };

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    onSubmit(formValues);
  };

  return {
    formValues,
    handleChange,
    handleSubmit,
  };
};

export default useModalForm;

You can then use this hook in any Modal that needs form functionality—no need to rewrite form state handling each time.

React元素对应的TypeScript类型

Absolutely! TypeScript provides built-in types for React elements:

  • React.ReactNode: This is the most flexible type—it represents any renderable content, including JSX elements, strings, numbers, null, undefined, booleans, or arrays of these values. Use this when your component accepts arbitrary content (like the children prop in our BaseModal above).

    Example:

    interface CardProps {
      content: React.ReactNode;
    }
    
  • React.ReactElement: This is a more specific type that represents a single React element (e.g., <Button /> or <div />). It excludes primitive values like strings or numbers. Use this when you want to enforce that a prop is exactly a React component instance.

    Example:

    interface ModalTriggerProps {
      trigger: React.ReactElement;
    }
    
  • JSX.Element: This is equivalent to React.ReactElement<any, any>—it's a shorthand for a React element without specifying the props or type.

In most cases, React.ReactNode is the go-to type for accepting renderable content, while React.ReactElement is useful when you need stricter type checking for component instances.

内容的提问来源于stack exchange,提问作者vanegeek

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 06:58:16