Home > Net >  How to conditionally render HTML input elements based on a string value that specifies its element t
How to conditionally render HTML input elements based on a string value that specifies its element t

Time:10-09

I am building a form using an array of objects that describe the elements within the form.

const fields = [
  { name: "Name", type: "text", placeholder: "What's your fullname?" },
  { name: "Email", type: "email", placeholder: "We promise not to spam you." },
  { name: "Password", type: "password", placeholder: "Make it a good one!" },
  { name: "About", type: "textarea", placeholder: "Tell us a little about yourself ..." },
];

I map over each object to produce my form which all works as desired.

{fields.map((field, index) => (
    <div key={form-field-${index}`}>
        <label htmlFor={field.name}>{field.name}</label>
        <div>
            {field.type !== "textarea" &&
                <input
                    type={field.type}
                    id={field.name}
                    name={field.name}
                    placeholder={field.placeholder}
                />
            }
            {field.type === "textarea" &&
                <textarea
                    id={field.name}
                    name={field.name}
                    placeholder={field.placeholder}
                />
            }
        </div>
    </div>
))}

As you can see I have some conditional rendering based on the type value of each field. For two different form elements this is not horrendous, but if I go adding other form elements (<select> for example), I would prefer to not have x conditionals if there is a better alternative.

Is there a better alternative? Move this to its a function, or its own component perhaps?

I am kind of hoping there is a means of doing something like:

{fields.map((field, index) => {
  <field.formType
    id="{field.name}"
    ...
    />
});

Where field.formType maps to a formType: "input" in the fields array.

CodePudding user response:

You can actually do this if you assign it to a capitalised variable then use that (is a weird JSX gotcha). See https://reactjs.org/docs/jsx-in-depth.html#user-defined-components-must-be-capitalized.

{fields.map((field, index) => {
  const Tag = field.formType
  return <Tag 
    id={field.name}
    ...
    />
});

CodePudding user response:

I would say create separate components for each type of input (input, textarea, select, etc.) and keep using conditionals:

{fields.map((field, index) => (
    <div key={form-field-${index}`}>
        <label htmlFor={field.name}>{field.name}</label>
        <div>
            {field.type === "text" && <Input field={field} />}
            {field.type === "email" && <Input field={field} />}
            {field.type === "password" && <Input field={field} />}
            {field.type === "textarea" && <Textarea field={field} />}
            {field.type === "select" && <Select field={field} />}
        </div>
    </div>
))}

or if you can change the structure of the data in the array, add a property like formType and use that to define which component to use:

{fields.map((field, index) => (
    <div key={form-field-${index}`}>
        <label htmlFor={field.name}>{field.name}</label>
        <div>
            {field.formType === "input" && <Input field={field} />}
            {field.formType === "textarea" && <Textarea field={field} />}
            {field.formType === "select" && <Select field={field} />}
        </div>
    </div>
))}

It's clean and you can use individual logic/state/etc. inside each component if needed

CodePudding user response:

You can provide a new field (property) like Component to your array of fields and there you can have anything you like, like Component:input, Component:select, Component: CustomComponent and then when mapping you render that component and provide other fields as props like: (inside map here)

const {Component, ...props} = field;

return <Component {...props}/>

note that for select you might need to create a custom component that when receives options as props knows how to handle them since select takes options as children and not as attributes (props)

  • Related