React.js - Show/Hide dynamically generated elements from component

13,118

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'}`} />
Share:
13,118
Paul Redmond
Author by

Paul Redmond

Updated on June 30, 2022

Comments

  • Paul Redmond
    Paul Redmond almost 2 years

    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')
      );