In my codebase I have a higher-order component (HOC) I use to add all the input validation functionality to a given component. It works great when used on a defined component like so...
let NameInput = React.createClass({
render() {
return (
<div>
<label htmlFor="name-input">Name</label>
<input name="name-input" />
</div>
);
}
});
let NameInput = addInputValidation(NameInput);
module.exports = NameInput;
But I now have a need to define a series of inputs based on an array from the server. Something like this...
let TestApp = React.createClass({
render() {
// Pretend the names array came from the server and is actually an array of objects.
let names = ['First name', 'Middle name', 'Last name'];
// Map over our names array in hopes of ending up with an array of input elements
let nameComponents = names.map((name, index) => {
let componentToRender = (
<div key={index}>
<label htmlFor={name}>{name}</label>
<input name={name} />
</div>
);
// Here is where I'd like to be able to use an HOC to wrap my name inputs with validation functions and stuff
componentToRender = addInputValidation(componentToRender);
return componentToRender;
})
return (
<div>
<p>Enter some names:</p>
{nameComponents}
</div>
);
}
})
let addInputValidation = function(Component) {
let hoc = React.createClass({
getInitialState() {
return {
isValid: false
};
},
render() {
return (
<div>
<Component {...this.props} />
{this.state.isValid ? null : <p style={{color: "red"}}>Error!!!!</p>}
</div>
);
}
});
return hoc;
}
module.exports = TestApp;
React doesn't like it when you try to render the result of calling an HOC from within another component.
I assume it has something to do with the fact that my componentToRender
isn't really a React component or something.
So my questions are...
Why can't I call a HOC from within another component?
Is there a way to call a HOC on each element of an array?
Here's a jsfiddle that might help: https://jsfiddle.net/zt50r0wu/
EDIT TO CLARIFY SOME THINGS:
The array that I'm mapping over is actually an array of objects that describe the details of the input. Including the type of input (select, checkbox, text, etc).
Also my addInputValidation
HOC actually takes more arguments than just the component. It takes an array of store indexes that will be pulled from the Redux store to be used for validation. These store indexes are derived from information in the array of objects describing the inputs. Having access to this potentially dynamic array is the reason I want to be able to call my HOC within the React life-cycle.
So mapping over my array of inputs might look more like this...
let Select = require('components/presentational-form/select');
let Text = require('components/presentational-form/select');
let CheckboxGroup = require('components/presentational-form/select');
let TestApp = React.createClass({
render() {
// Pretend the inputs array came from the server
let inputs = [{...}, {...}, {...}];
// Map over our inputs array in hopes of ending up with an array of input objects
let inputComponents = inputs.map((input, index) => {
let componentToRender = '';
if (input.type === 'select') {
componentToRender = <Select labelText={input.label} options={input.options} />;
} else if (input.type === 'text') {
componentToRender = <Text labelText={input.label} />;
} else if (input.type === 'checkbox') {
componentToRender = <CheckboxGroup labelText={input.label} options={input.options} />;
}
// Here is where I'd like to be able to use an HOC to wrap my name inputs with validation functions and stuff
componentToRender = addInputValidation(componentToRender, input.validationIndexes);
return componentToRender;
})
return (
<div>
<p>Enter some names:</p>
{inputComponents}
</div>
);
}
})
Regarding your edit: The problem is still that you are returning a component instead of an element from the .map
callback. But that can be easily solved by changing
return componentToRender;
to
return React.createElement(componentToRender);
The problems in your code are:
addInputValidation
expects to be passed a component but you are passing it an element ( <div />
). It seems the simplest way to solve your problem would be create a generic Input
component that accepts the name and value as prop:
let Input = React.createClass({
render() {
return (
<div>
<label htmlFor={this.props.name}>{this.props.name}</label>
<input name={this.props.name} />
</div>
);
}
});
module.exports = addInputValidation(Input);
Which is used as
let nameComponents = names.map((name, index) => <Input key={index} name={name} />);
The thing I think you're tripping over is the distinction between a component vs. an element. I find it helpful to think of a component as a function and an element as the result of that function. So all you're really trying to do is conditionally choose one of three different functions, pass it some arguments, and the display the results. I believe you want something like this:
(this can be cleaned up, BTW, just tried to preserve your code structure as much as possible)
let Select = require('components/presentational-form/select');
let Text = require('components/presentational-form/select');
let CheckboxGroup = require('components/presentational-form/select');
let TestApp = React.createClass({
render() {
// Pretend the inputs array came from the server
let inputs = [{...}, {...}, {...}];
// Map over our inputs array in hopes of ending up with an array of input objects
let inputComponents = inputs.map((input, index) => {
let ComponentToRender;
let props;
if (input.type === 'select') {
ComponentToRender = addInputValidation(Select);
props = { labelText: input.label, options: input.options };
} else if (input.type === 'text') {
ComponentToRender = addInputValidation(Text);
props = { labelText: input.label };
} else if (input.type === 'checkbox') {
ComponentToRender = addInputValidation(CheckboxGroup);
props = { labelText: input.label, options: input.options };
}
let element = <ComponentToRender {...props} />;
return element;
})
return (
<div>
<p>Enter some names:</p>
{inputComponents}
</div>
);
}
})
Yes, there is a way of course. But HOC is quite a painful way to handle validation in general.
There is a different approach to validation , based on ValueLink pattern .
Just compare the resulting code to higher order components approach.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.