I'm generating form field elements using a component template importing an array of data. I want to be able to hide some of these elements and show them when some other's element's criteria has been met; this is fairly common in form fields, e.g. When you select item A, form field X appears, when you select item B, form field X is hidden.
I've read a fair bit on conditional rendering on the React docs site but these examples don't really work for what I'm doing although the Preventing Component from Rendering section is close.
I made a Codepen demoing my setup, what I want to do is show the second field if the criteria for the first field is met (in this example, the first field should have 5 characters entered). I've passed through a prop to set the initial hiding but how can I find that specific hidden element and unhide it?
// Field data
const fieldData = [{
"type": "text",
"label": "First",
"name": "first",
"placeholder": "Enter first name",
"hidden": false
}, {
"type": "text",
"label": "Surname",
"name": "surname",
"placeholder": "Enter surname",
"hidden": true
}];
// Get form data
class FormData extends React.Component {
constructor(props) {
super(props);
this.state = {
items: props.data
};
}
render() {
let els = this.state.items;
return els.map((el, i) => {
return <Input key={i} params={el} />
});
}
}
// Input builder
class Input extends React.Component {
constructor(props) {
super(props);
// Keep state value
this.state = {
value: '',
valid: false,
hidden: this.props.params.hidden
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.setState({
value: e.target.value,
valid: e.target.value.length < 5 ? false : true
});
}
render() {
// Element attributes
const {type, label, name, placeholder, hidden} = this.props.params;
const isValid = this.state.valid === true ? <span>Valid! Should show Surname field.</span> : <span>Not valid. Should hide Surname field.</span>;
if (!hidden) {
return (
<div>
{label ? <label htmlFor={name}>{label}</label> : null}
<input
type={type}
name={name}
placeholder={placeholder || null}
value={this.state.value}
onChange={this.handleChange}
/>
{isValid}
</div>
);
} else {
return null;
}
}
}
// App
class App extends React.Component {
render() {
return (
<div>
<h1>Show/Hide test</h1>
<p>What we want here is the surname to appear when firstname has a value (say, it has 5 characters) and hide surname when firstname doesn't.</p>
<FormData data={fieldData} />
</div>
);
}
}
ReactDOM.render(
<form>
<App />
</form>,
document.getElementById('app')
);
You can lift the state up so a parent of the Input
will handle the state and validations.
You can conditionally invoke the "validator" of the surname
property or any other property only if it exists and do a convention with yourself that a validate method will get the name of: propertNameValidator
.
So for example when you do the loop over the inputs, you could check if there is
a validate method named surnameValidator
and invoke it against the hidden
prop that you will pass the Input
, if it is not exists then just pass false
.
Here is a small example with your code:
// Field data
const fieldData = [
{
type: "text",
label: "First",
name: "first",
placeholder: "Enter first name",
hidden: false
},
{
type: "text",
label: "Surname",
name: "surname",
placeholder: "Enter surname",
hidden: true
}
];
// Get form data
class FormData extends React.Component {
constructor(props) {
super(props);
this.state = {
items: props.data.map(el => ({...el, value: ''})) // add the value property
};
}
onInputChange = (inputId, value) => {
const { items } = this.state;
const nextState = items.map((item) => {
if (inputId !== item.name) return item;
return {
...item,
value,
}
});
this.setState({ items: nextState });
}
surnameValidator = () => {
const { items } = this.state;
const nameElement = items.find(item => item.name === 'first');
return nameElement && nameElement.value.length >= 5
}
render() {
let els = this.state.items;
return (
<div>
{
els.map((el, i) => {
const validator = this[`${el.name}Validator`];
return (
<Input
key={i}
{...el}
inputId={el.name}
hidden={validator ? !validator() : el.hidden}
onInputChange={this.onInputChange}
/>
);
})
}
</div>
)
}
}
// Input builder
class Input extends React.Component {
handleChange = ({ target }) => {
const { inputId, onInputChange } = this.props;
onInputChange(inputId, target.value);
}
render() {
// Element attributes
const { type, label, name, placeholder, hidden, value } = this.props;
return (
<div>
{
hidden ? '' : (
<div>
{label ? <label htmlFor={name}>{label}</label> : null}
<input
type={type}
name={name}
placeholder={placeholder || null}
value={value}
onChange={this.handleChange}
/>
</div>
)
}
</div>
);
}
}
// App
class App extends React.Component {
render() {
return (
<div>
<h1>Show/Hide test</h1>
<p>
What we want here is the surname to appear when firstname has a value
(say, it has 5 characters) and hide surname when firstname doesn't.
</p>
<FormData data={fieldData} />
</div>
);
}
}
ReactDOM.render(
<form>
<App />
</form>,
document.getElementById("app")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
If you already created the element and you just want to hide / show it, then you can conditionally add or remove a CSS class
that hides it.
<input className={`${!isValid && 'hide'}`} />
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With