I am trying to write a reusable form dialog in TypeScript and MaterialUI. I want to be able to pass in as a prop to the dialog an array of Form components that the dialog will render. Inside the dialog, a state index will track the current form and render it. But I can't get it to work. Here is the code I have so far:
interface CustomFormProps {
hasNext: boolean,
hasPrev: boolean,
hasSubmit: boolean
}
const FormOne:React.ComponentType<CustomFormProps> = ({hasNext= true, hasPrev= true, hasSubmit= true}) => {
return (
<Box>
<Typography>
{hasNext ? "NEXT" : ""}
{hasPrev ? "PREV" : ""}
{hasSubmit ? "SUBMIT" : ""}
</Typography>
</Box>
)
}
interface FormDialogProps {
toggleDialog: () => void | undefined,
isOpen: boolean,
headerText: string,
stepLabels: Array<string>,
steps: React.ComponentType<CustomFormProps>[]
}
const FormDialog = (props: FormDialogProps) => {
const theme = useTheme();
const [currentForm, setCurrentForm] = useState(0);
const next = () => {
}
const previous = () => {
}
const submit = () => {
}
const Current:React.ComponentType<CustomFormProps> = props.steps[currentForm];
return (
<Dialog onClose={props.toggleDialog} open={props.isOpen} fullWidth maxWidth="md">
<DialogTitle sx={{ borderBottom: `1px solid ${theme.colors.gold.neutral}`, backgroundColor: theme.colors.gold.neutral, padding: "20px" }}>
<strong>{props.headerText}</strong>
</DialogTitle>
<DialogContent sx={{ marginTop: "12px", display: "flex", flexDirection: "column" }}>
<Current />
</DialogContent>
<DialogActions sx={{ borderTop: `1px solid ${theme.colors.gold.neutral}`, backgroundColor: theme.colors.gold.neutral, padding: "20px" }}>
<Button variant="outlined" onClick={props.toggleDialog} sx={{ color: "black" }}>Previous</Button>
<Button variant="outlined" onClick={props.toggleDialog} sx={{ color: "black" }}>Next</Button>
<Button variant="outlined" onClick={props.toggleDialog} sx={{ color: "black" }}>Submit</Button>
</DialogActions>
</Dialog>
);
};
The issue I am having is in the FormDialog component where I try to render the current child. TypeScript is saying I haven't provided the required props, but I think those props do exist where I define FormOne. If I have to redefine the props inside the FormDialog, that sort of completely defeats the purpose of being able to pass in components to the dialog that manage their own props. Is there a way around this?
The main issue is you're using React.ComponentType<CustomFormProps>
for both steps and Current props. In steps, you have passed the props. Though, for current-- you have no props passed: <Current />
. The props being defined in CustomFormProps
:
hasNext: boolean,
hasPrev: boolean,
hasSubmit: boolean
Thus,
const Current = props.steps[currentForm];
const isFirstStep = currentForm === 0;
const isLastStep = currentForm === props.steps.length - 1;
//passing the required props:
<Current
hasNext={!isLastStep}
hasPrev={!isFirstStep}
hasSubmit={isLastStep}
/>