The following code implements clickable Mui Button which upon click action opens the system’s "Select file" window. It’s usage is pretty straightforward and can be triggered as <SelectFileButton onFileSelected={(selectedFiles) => { ... }}
The problem that I’m facing right now is that I’m trying to make it more generic, enabling any "clickable" type to be used in place of the Mui Button (line: <Button onClick={handleClick}>{children}</Button>
) and of course I’m failing miserably with this daunting task. Is there any simple solution that can change mentioned line of code to more generic equivalent?
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>
);
};
3
Answers
I've ended up with the following solution. When I create SelectFileButton component I pass in an additional prop named
component
with the Mui component of my choice, for exampleYou can pass any element as prop
or you can delete onClick prop and pass element with this prop:
Of course you should change prop types depends on solution you will choose.
But I think you want make over optimization.
https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it
This component have one simple function and include all to implement it. A lot of dependencies make component hard to use and understand. And component what you can pass may have very different props and behaviour in comparison with Button that will be hard to handle it.
The MUI Button has a
component
prop that will accept anelementType
(any HTML element or other component)This prop could be forwarded through your props
And then consumed by the MUI button
This way, the MUI implementation will ensure that proper button-like accessibility is applied to whichever elementType is passed in