Reputation: 3841
I'm trying to build an input-component in ReactJs
using typescript.
An input can have different types, e.g. text
, date
, select
or textarea
.
Depending on the given type the rendered control can be different. If set to text
, <input type="text" />
should be returned.
On the other hand, if the type is select
, <select></select>
should be returned.
These are my props:
export interface IInputProps {
type?: "text" | "checkbox" | "date" | "select"| "textarea";
}
In my render
-function I now try to figure out, what should be returned:
render(): JSX.Element {
const { type } = this.props;
let Element: JSX.Element = null;
switch (type) {
case "text":
Element = <input type={type} />;
break;
case "select":
const { children } = this.props.selectProps;
Element = <select>{children}</select>;
break;
default:
throw new Error(`The type ${type} is currently not implemented. If needed, do so.`);
}
const classes = classNames("form-control");
// return the markup of the final element
return (
<React.Fragment>
{ /* below the error is thrown */ }
<Element ref={/* some internal handling here */} className={classes} />
</React.Fragment>
);
}
However, this code throws
Error TS2604 (TS) JSX element type 'Element' does not have any construct or call signatures.
Note that the example is a simplified version of the problem. There will be more type-specific properties, e.g. attributes rows
and cols
for textarea
.
What would be the correct way to achieve this? What type must Element
be? Also note, that I have to set attributes, which may not be available, e.g. type
is not available on <select>
.
Upvotes: 1
Views: 2070
Reputation: 20614
You're doubling up on React.createElement
calls. Remember that:
let element = <div />
turns into this:
let element = React.createElement('div')
so your code:
Element = <input type={type} />;
and:
<Element ref={/* some internal handling here */} className={classes} />
is like doing:
let Element = React.createElement(React.createElement('input'))
which doesn't make sense.
Solution:
1) evaluate your already created element:
return (
<React.Fragment>
{Element}
</React.Fragment>
);
This means any props you want to add, like ref
, you'll need to add when creating the element:
Element = <input type={type} ref={...} />;
This may seem arduous, luckily you can just make a component on the fly:
// notice the function, not directly calling createElement
Element = props => <input type={type} {...props} />;
Then this code will work:
return (
<React.Fragment>
<Element ref={/* some internal handling here */} className={classes} />
</React.Fragment>
);
update: I almost never use ref
so I forgot about the stateless warning. quick fix:
// notice the component, not directly calling createElement
Element = class extends React.Component {
render() {
return <input type={type} {...this.props} />;
}
}
Upvotes: 1