reactjs - 使用 Typescript 使 React 的 "选择文件" Mui 按钮组件通用化
以下代码实现了可点击的 Mui 按钮,点击后会打开系统的“选择文件”窗口。它的用法非常简单,可以通过 <SelectFileButton onFileSelected={(selectedFiles) => { ... }} 触发。
我现在面临的问题是,我试图让它更通用,使任何“可点击”类型都可以替代 Mui Button(行:<Button onClick={handleClick}>{children}</Button>),当然,我在这个艰巨的任务上惨败了。有没有一种简单的解决方法,可以将上述代码行更改为更通用的等效形式?
import React from "react";
import Button from "@mui/material/Button";
import { ChangeEvent, ReactNode } from "react";
type Props = {
onFileSelected?: (selectedFiles: File | File[]) => void;
multiple?: boolean;
children: ReactNode;
};
export const SelectFileButton: React.FC<Props> = ({
children,
onFileSelected = null,
multiple = false,
}) => {
const hiddenFileInput = React.useRef<HTMLInputElement>(null);
const handleFileChange = (e: ChangeEvent<HTMLInputElement>) => {
if (e.target.files)
onFileSelected?.(
multiple ? Array.from(e.target.files) : e.target.files[0]
);
};
const handleClick = () => {
hiddenFileInput.current!.click();
};
return (
<React.Fragment>
<Button onClick={handleClick}>{children}</Button>
<input
type="file"
ref={hiddenFileInput}
style={{ display: "none" }}
onChange={handleFileChange}
multiple={multiple}
/>
</React.Fragment>
);
};
答案#1
你可以将任何元素作为属性传递
export const FileUploadButton: React.FC<Props> = ({
children,
onFileSelected = null,
multiple = false,
ClickableElement,
onClick,
}) => {
const hiddenFileInput = React.useRef<HTMLInputElement>(null)
const handleFileChange = (e: ChangeEvent<HTMLInputElement>) => {
if (e.target.files)
onFileSelected?.(
multiple ? Array.from(e.target.files) : e.target.files[0],
)
}
const handleUploadClick = () => {
hiddenFileInput.current!.click()
}
return (
<React.Fragment>
<ClickableElement onClick={handleUploadClick}>
{children}
</ClickableElement>
<input
type="file"
ref={hiddenFileInput}
style={{ display: 'none' }}
onChange={handleFileChange}
multiple={multiple}
/>
</React.Fragment>
)
}
<FileUploadButton children="text" ClickableElement={Button} onClick={() => {....}/>
或者你可以删除 onClick 属性,然后传入带有此属性的元素:
<FileUploadButton children="text" ClickableElement={<Button onClick={() => {....} />} />
当然,你应该根据你选择的解决方案更改属性类型。
但我认为你想过度优化。
https://en.wikipedia.org/wiki/You_aren't_gonna_need_it
这个组件有一个简单的功能,并且包含实现它所需的全部内容。许多依赖使组件难以使用和理解。而且你可以传递的组件可能与按钮相比具有非常不同的属性和行为,这将很难处理。
答案#2
MUI Button 有一个 component 属性,它可以接受一个 elementType(任何 HTML 元素或其他组件)
这个属性可以通过你的属性传递
type Props = {
onFileSelected?: (selectedFiles: File | File[]) => void;
multiple?: boolean;
component?: React.ElementType
children: ReactNode;
};
然后被 MUI 按钮消耗
<Button component={component} onClick={handleUploadClick}>{children}</Button>
这样,MUI 的实现将确保对传入的任何 elementType 应用适当的类似按钮的可访问性
