I'm looking for the proper pattern/way to develop this with React, but I didn't find anything relevant/elegant so far.
I basically want to write a form engine. For each input of the form, I want some generic behaviors.
I've read on React documentation that inheritance isn't the proper way to go; the good way to go is to design a generic component and specialize it by composition.
In my top component, I want to write something like:
<Form>
<TextInput .../>
<TextInput .../>
<EmailInput .../>
</Form>
Each type of input must basically do always the same things: example: check this value against its validators, etc.
So, I've designed a generic component FormInput
which contains all those standard behaviors. When I write my TextInput
component, here is what it looks like:
export default class TextInput extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<FormInput>
<input name={this.props.name}
type='text'
onChange={this.onChange}
onBlur={this.dirty}
value={this.state.value}
/>
</FormInput>
);
}
}
Now, the trouble is that this.onChange
or this.dirty
are the standard behaviors located in the FormInput
component, so obviously, I cannot access directly to them like that...
What is the proper way to connect the transcluded content to its container component?
EDIT
Just to clarify and sum up the goal, I basically want to make a generic component and a transcluded content/template. The issue with this is that I need to bind the specific DOM template (which is in the specific component) to the generic handlers (which are in the generic component).
Thanks by advance!
I've finally figured it out!
Here is my solution:
export default class TextInput extends React.Component {
constructor(props) {
super(props);
}
template(state, formInput) {
return (
<input name={this.props.name}
type='text'
onChange={formInput.onChange}
onBlur={formInput.dirty}
value={state.value}/>
);
}
render() {
return (
<FormInput {...this.props} template={this.template} />
);
}
}
It was finally easier than expected, but I didn't use transclusion.
The FormInput
component hosts everything, from the state to the common code/behaviors (like dirty
or onChange
).
The TextInput
component only instanciates a FormInput
and hosts a function called template
, that takes the remote state and the generic FormInput
component itself to access its functions.
Then in the FormInput
, i've got the following:
render() {
return this.props.template(this.state, this);
}
The generic input component calls the template
function and passes in what is needed for the rendering.
This way, I uncouple the view from its behavior.
Hope you'll like it and that it will helps someone later ;)