Search code examples
reactjsreact-reduxredux-form

How to dynamically build a Redux form?


I'm looking to learn how to dynamically build a redux-form. The idea being, the component called, componentDidMount goes at fetches a list of items from the server and inserts them in the store, store.my_items:

{'item1', 'itemB', 'itemZZZ', etc...} 

Now that these items are in the store, I want to build the redux-form with a Field for each item in store.my_items.

Example, several of these dynamically created:

      <div>
        <label>ItemXXX</label>
        <div>
          <label>
            <Field name="ItemXXX" component="input" type="radio" value="1" />
            {' '}
            1
          </label>
          <label>
            <Field name="ItemXXX" component="input" type="radio" value="2" />
            {' '}
            2
          </label>
        </div>
      </div>

With React, redux-form, what would be the right way to approach building this type of dynamic Redux form?


Solution

  • I am following this approach for building a form dynamically. I am just declaring metadata of form fields (type, placeholder, the unique name of each field, etc.) like this:

    const fields = [
          { name: 'name', type: 'text', placeholder: 'Enter Name' },
          { name: 'age', type: 'number', placeholder: 'Enter age' },
          { name: 'email', type: 'email', placeholder: 'Enter Email' },
          { name: 'employed', type: 'checkbox' },
          {
            name: 'favouriteColors',
            type: 'select',
            options: [
              { label: 'Red', value: 'red' },
              { label: 'Yellow', value: 'yellow' },
              { label: 'Green', value: 'green' },
            ],
          },
        ]
    

    Now, I am just iterating over these fields and rendering input for each field like the way I have done in renderField component given below. My general form component looks like this:

    import React from 'react'
    import { Field, reduxForm } from 'redux-form/immutable'
    
    const renderField = ({ input, field }) => {
      const { type, placeholder } = field
      if (type === 'text' || type === 'email' || type === 'number' || type === 'checkbox') {
        return <input {...input} placeholder={placeholder} type={type} />
      } else if (type === 'select') {
        const { options } = field
        return (
          <select name={field.name} onChange={input.onChange}>
            {options.map((option, index) => {
              return <option key={index} value={option.value}>{option.label}</option>
            })}
          </select>
        )
      } else {
        return <div>Type not supported.</div>
      }
    }
    
    const SimpleForm = ({ handleSubmit, fields }) => {
      return (
        <div>
          {fields.map(field => (
            <div key={field.name}>
              <Field
                name={field.name}
                component={renderField}
                field={field}
                />
            </div>
          ))}
          <div onClick={handleSubmit}>Submit</div>
        </div>
      )
    }
    
    export default reduxForm({
      form: 'simpleForm'
    })(SimpleForm)
    

    and passing fields to SimpleForm component like this:

    <SimpleForm fields={fields} onSubmit={() =>{}}/>
    

    Now it's your choice that you want to fetch fields like this from the server or just want to fetch only items and make fields like this (by passing item as the name of field) on the frontend.

    By using this approach, I am able to re-use template based on given type.

    If someone has a better approach to make a form dynamically, then I would love to learn that too.

    Edit: If we have to pass form name dynamically, then a small change will be required:

    export default reduxForm({})(SimpleForm)
    

    and we can pass form name while this component like this:

    <SimpleForm form={'simpleForm'} fields={fields} onSubmit={() =>{}} />