React 相关问题

1. TypeScript泛型  
const mergedColumns = columns.map<TableProps<DataType>>((col) => {
  const mergedColumns: TableProps<DataType>['columns'] = columns.map((col) => {
 
Argument of type '(col: { title: string; dataIndex: string; width: string; editable: boolean; } | { title: string; dataIndex: string; width?: undefined; editable?: undefined; }) => { title: string; dataIndex: string; width: string; editable: boolean; } | { ...; } | { ...; }' is not assignable to parameter of type '(value: { title: string; dataIndex: string; width: string; editable: boolean; } | { title: string; dataIndex: string; width?: undefined; editable?: undefined; }, index: number, array: ({ title: string; dataIndex: string; width: string; editable: boolean; } | { ...; })[]) => TableProps<...>'.
Type '{ title: string; dataIndex: string; width: string; editable: boolean; } | { title: string; dataIndex: string; width?: undefined; editable?: undefined; } | { onCell: (record: any) => { record: any; dataIndex: string; title: string; }; title: string; dataIndex: string; width: string; editable: boolean; }' is not assignable to type 'TableProps<DataType>'.
Type '{ title: string; dataIndex: string; width: string; editable: boolean; }' is not assignable to type 'TableProps<DataType>'.
Types of property 'title' are incompatible.
Type 'string' is not assignable to type 'PanelRender<DataType>'.ts(2345)
(parameter) col: {
title: string;
dataIndex: string;
width: string;
editable: boolean;
} | {
title: string;
dataIndex: string;
width?: undefined;
editable?: undefined;
}
 
解答:
// 泛型 <TableProps<DataType>> 修饰的是"回调函数的返回值"
const result = columns.map<TableProps<DataType>>((col) => {
  // 必须返回 TableProps<DataType>
  return {
    dataSource: [],      // ✅ TableProps 有 dataSource
    columns: [],         // ✅ TableProps 有 columns
    pagination: false,   // ✅ TableProps 有 pagination
    // ... 所有属性都要有
  };
});



2. 写了一个hooks
import { DownOutlined } from '@ant-design/icons';
import { Dropdown, Space, type MenuProps } from 'antd';
import React from 'react';

const useOperation: React.FC = () => {
  const items: MenuProps['items'] = [
    {
      label: (
        <a href="https://www.antgroup.com" target="_blank" rel="noopener noreferrer">
          1st menu item
        </a>
      ),
      key: '0',
    },
    {
      label: (
        <a href="https://www.aliyun.com" target="_blank" rel="noopener noreferrer">
          2nd menu item
        </a>
      ),
      key: '1',
    },
    {
      type: 'divider',
    },
    {
      label: '3rd menu item',
      key: '3',
    },
  ];
  return (
    <>
      <Dropdown menu={{ items }} trigger={['click']}>
        <a onClick={(e) => e.preventDefault()}>
          <Space>
            Click me
            <DownOutlined />
          </Space>
        </a>
      </Dropdown>
    </>
  );
};
export default useOperation;


为什么引用时候,一直 Expected 1 arguments, but got 0.

解答:
// ❌ 错误:useOperation 被定义为一个 React 组件
const useOperation: React.FC = () => { ... }

// ✅ 正确:Hook 应该返回数据或方法,而不是 JSX
const useOperation = () => { ... }
3、这个hooks方法
import { PermissionName } from '@/common/consts';
import { formatMessage } from '@/common/messages';
import { MSG012, MSG031 } from '@/common/messages/messages';
import { checkRowEditable } from '@/common/utils/common';
import type { MasterEmployee } from '@/models';
import {
  ArrowDownOutlined,
  ArrowUpOutlined,
  CloseCircleOutlined,
  DeleteOutlined,
  EditOutlined,
  ExclamationCircleFilled,
  MenuOutlined,
  SaveOutlined,
} from '@ant-design/icons';
import { App, Button, Dropdown, type ButtonProps, type MenuProps } from 'antd';
import { useEffect, useMemo, useRef } from 'react';

interface DeleteConfirmOptions<T> {
  /** Title prefix, e.g. "行" -> "行削除確認" */
  title: string;
  /** Content subject or function to get content from record, e.g. "行" -> "行を削除してもよろしいでしょうか?" */
  content: string | ((record: T) => string);
}

interface ColumnProps<T> {
  employee?: MasterEmployee;
  fromScreen?: string;
  size?: ButtonProps['size'];
  editingKey: string | number | null;
  rowKey: keyof T;
  onSave: (record: T) => void;
  onCancel: (record: T) => void;
  onEdit: (record: T) => void;
  onDelete: (record: T) => void;
  onAddBefore?: (record: T) => void;
  onAddAfter?: (record: T) => void;
  /** Custom delete confirmation dialog options */
  deleteConfirm?: DeleteConfirmOptions<T>;
  /** When false, disables the operation dropdown menu */
  canSave?: boolean; // buttonPermissions
  deleteMenuLabel?: string;
}

/**
 * Hook that returns an operation column renderer for table.
 * Uses App.useApp() to get modal and message instances that can consume ConfigProvider context.
 */
const useOperationColumn = <T extends object>(options: ColumnProps<T>) => {
  const { modal, message } = App.useApp();
  const { size = 'small' } = options;
  const { deleteMenuLabel = '行を削除' } = options;

  // Track active delete confirmation modal to destroy on unmount
  const activeModalRef = useRef<ReturnType<typeof modal.confirm> | null>(null);

  // Destroy confirmation modal when component unmounts (e.g. browser back navigation)
  useEffect(() => {
    return () => {
      activeModalRef.current?.destroy();
    };
  }, []);

  // SC050 取引管理室
  const canShowDelete = useMemo(() => {
    return options?.fromScreen === 'SC050' ? options?.employee?.permissionName === PermissionName.ALL : true;
  }, [options]);

  const hasTeamPermission = (record: T): boolean => {
    if (!options.employee) {
      return true;
    }
    return checkRowEditable(record, options.employee);
  };

  return (_: unknown, record: T) => {
    const isEditing = record[options.rowKey] === options.editingKey;
    const baseDisabledRule = !options.canSave || (!!options.editingKey && !isEditing);
    const getDisabledResult = (key: string): boolean => {
      if (key === 'delete') {
        return record['permissionName']
          ? record['permissionName'] === PermissionName.ALL // permission 取引管理室
          : baseDisabledRule || !hasTeamPermission(record);
      }
      if (key === 'edit') {
        return baseDisabledRule || !hasTeamPermission(record);
      }
      return baseDisabledRule;
    };
    // Show delete confirmation dialog
    const showDeleteConfirm = () => {
      const content = `${formatMessage(MSG031)}`;

      activeModalRef.current = modal.confirm({
        title: MSG031.title,
        icon: <ExclamationCircleFilled />,
        content,
        okText: 'OK',
        cancelText: 'キャンセル',
        onOk() {
          activeModalRef.current = null;
          options.onDelete(record);
        },
        onCancel() {
          activeModalRef.current = null;
        },
      });
    };

    const editingItems: MenuProps['items'] = [
      {
        key: 'save',
        label: '行を保存',
        icon: <SaveOutlined />,
        onClick: () => options.onSave(record),
      },
      {
        key: 'cancel',
        label: 'キャンセル',
        icon: <CloseCircleOutlined />,
        onClick: () => options.onCancel(record),
      },
    ];

    const normalItems: MenuProps['items'] = [
      {
        key: 'edit',
        label: '行を編集',
        icon: <EditOutlined />,
        disabled: getDisabledResult('edit'),
        onClick: () => options.onEdit(record),
      },
      ...(options.onAddBefore
        ? [
            {
              key: 'addBefore',
              label: '上に行を追加',
              icon: <ArrowUpOutlined />,
              disabled: getDisabledResult('addBefore'),
              onClick: () => {
                options.onAddBefore?.(record);
                message.success(MSG012.template);
              },
            },
          ]
        : []),
      ...(options.onAddAfter
        ? [
            {
              key: 'addAfter',
              label: '下に行を追加',
              icon: <ArrowDownOutlined />,
              disabled: getDisabledResult('addAfter'),
              onClick: () => {
                options.onAddAfter?.(record);
                message.success(MSG012.template);
              },
            },
          ]
        : []),
      ...(canShowDelete
        ? [
            {
              key: 'delete',
              danger: true,
              label: deleteMenuLabel,
              icon: <DeleteOutlined />,
              disabled: getDisabledResult('delete'),
              onClick: showDeleteConfirm,
            },
          ]
        : []),
    ];

    const menuItems = isEditing ? editingItems : normalItems;

    return (
      <Dropdown menu={{ items: menuItems }} trigger={['click']} disabled={baseDisabledRule}>
        <Button type="link" size={size} icon={<MenuOutlined />} />
      </Dropdown>
    );
  };
};

export default useOperationColumn;

 解答:
1. antd, Table操作栏必须返回一个函数,所以当前hooks,return 了一个函数。 而且record属性,是默认自带的。 像下面这种。 

{
title: 'operation',
dataIndex: 'operation',
render: (_: any, record: DataType) => {

2. hooks ,下面 _: unknown 是用来占位的,不使用这个参数
return (_: unknown, record: T) => {

3. 解释泛型
const useOperationColumn = <T extends object>(options: ColumnProps<T>) 
const useOperationColumn = <T>(options: ColumnProps<T>) => {
  return (_, record: T) => {
    // T 可以是任何类型
  };
};

// 用于 User
useOperationColumn<User>({ ... });

// 用于 Product
useOperationColumn<Product>({ ... });

// 用于任何类型
useOperationColumn<Order>({ ... });
// 定义三个不同的类型
interface User {
  id: number;
  name: string;
  email: string;
}

interface Product {
  id: number;
  title: string;
  price: number;
}

interface Order {
  id: number;
  userId: number;
  total: number;
  status: 'pending' | 'completed';
}
type ColumnProps<T> = { fromScreen?: string; employee?: any; deleteMenuLabel?: string; size?: 'small' | 'middle' | 'large'; onEdit?: (record: T) => void; // ← 用到了 T onDelete?: (record: T) => void; // ← 用到了 T

里面用到了T,所以ColumnProps<T> 要加<T> , T是占位符,这个地方加上T之后,所以前面必须要写<T extends object> 这个来先声明T

 

问题4. 

rowKey: keyof T;
<T>(options: ColumnProps<T>)   这样直接写报错  JSX element 'T' has no corresponding closing tag.ts(17008)  Cannot find name 'T'.
 
问题5:
const [editingKey, setEditingKey] = useState<number | null>(null);
  const isRowEditing = (record: DataType) => record.id === editingKey;
 
  const handleEdit = (record: DataType) => {
    console.log('编辑方法');
    form.setFieldsValue({ ...record });
    setEditingKey(record.id!);
  };
  const handleCancel = () => {
    setEditingKey(null);
  };
posted @ 2026-09-08 12:42  小兔子09  阅读(2)  评论(0)    收藏  举报