Typescript & React Component that accepts onChange for both TextArea and Input
You can use a discriminated union to pass in two types of arguments, one for text
and the other for textarea
. This has the added bonus of ensuring the handler and the type are in sync.
type InputProps = { // The common Part
className?: string;
placeholder?: string;
} & ({ // The discriminated union
type?: "text";
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
} | {
type: "textarea";
onChange?: (e: React.ChangeEvent<HTMLTextAreaElement>) => void;
})
const Input: FunctionComponent<InputProps> = (props: InputProps) => {
if (props.type === 'textarea') {
return <textarea {...props} />;
}
return <input {...props} />;
};
class Usage extends React.Component<State> {
state: State;
onInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
this.setState({ input: e.target.value });
};
render() {
return (
<Input placeholder="Write an something..." onChange={this.onInputChange} />
);
}
}
You need to create a class incase you are using typescript.
The normal function does not allows the |
in the typescript prop types.
This should be your Input.js
file:
export interface IInputProps {
className?: string;
type?: string;
placeholder?: string;
onChange?: (e?: React.ChangeEvent<HTMLInputElement> | React.ChangeEvent<HTMLTextAreaElement>) => void;
}
export class Input extends React.Component<IInputProps, {}> {
constructor(props: IInputProps) {
super(props);
}
render() {
const { props, props: { type } } = this;
if (type === 'textarea') {
return <textarea {...props} />;
}
return <input type={type} {...props} />;
}
}
and here is how it can be used:
class Usage extends React.Component<State> {
state: State;
onInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
this.setState({ input: e.target.value });
};
render() {
return (
<Input placeholder="Write an something..." onChange={this.onInputChange} />
);
}
}
This is how it will evaluate if it is a Input
or a TextArea
: