0
votes

Struggling with this for two days already. In a 'redux-form' form, I need to prepopulate an order field with a value that comes from array.map iteration index. Here is a complete code for my form (please see comments):

const renderField = ({ input, label, type, meta: { touched, error } }) => {
  let color = 'normal';
  if (touched && error) {
    color = 'danger';
  }

  return (
    <FormGroup color={color}>
      <Label>{label}</Label>
      <div>
        <input {...input} type={type} placeholder={label} />
        {touched && (error && <FormFeedback>{error}</FormFeedback>)}
      </div>
    </FormGroup>
  );
};

const renderChoices = ({ fields, meta: { error } }) => (
  <ul>
    <li>
      <button type="button" onClick={() => fields.push()}>
        Add Choice
      </button>
    </li>
    {fields.map((choice, index) => (
      <li key={index}>
        <button type="button" title="Remove Choice" onClick={() => fields.remove(index)}>
          x
        </button>
        <Field name={choice} type="text" component={renderField} label={`Choice #${index + 1}`} />
      </li>
    ))}
    {error && <li className="error">{error}</li>}
  </ul>
);

const renderQuestion = ({ fields, meta: { error, submitFailed } }) => (
  <ul>
    <li>
      <Button type="button" onClick={() => fields.push({})}>
        Add Question
      </Button>
      {submitFailed && error && <span>{error}</span>}
    </li>
    {fields.map((question, index) => (
      <li key={index}>
        <button type="button" title="Remove Question" onClick={() => fields.remove(index)}>
          x
        </button>
        <h4>Question #{index + 1}</h4>
        <Field // this is the field that needs to be prepopulated
          name={`${question}.order`}
          type="text"
          component={renderField}
          label="Order"
        />
        <Field name={`${question}.prompt`} type="text" component={renderField} label="Prompt" />
        <FieldArray name={`${question}.choices`} component={renderChoices} />
      </li>
    ))}
  </ul>
);

const QuizStepAddForm = props => {
  const { handleSubmit, pristine, reset, submitting } = props;
  return (
    <Form onSubmit={handleSubmit}>
      <Field name="order" type="number" component={renderField} label="Quiz Order" />
      <Field name="title" type="text" component={renderField} label="Quiz Title" />
      <FieldArray name="questions" component={renderQuestion} />
      <div>
        <Button style={{ margin: '10px' }} color="primary" type="submit" disabled={submitting}>
          Submit
        </Button>
        <Button type="button" disabled={pristine || submitting} onClick={reset}>
          Clear Values
        </Button>
      </div>
    </Form>
  );
};

export default reduxForm({
  form: 'quizStepAddForm',  
})(QuizStepAddForm);

I have tried to use redux-form Field API meta props, meta:initial to initialize the field, but by just setting it in the Field tag does not change anything. I also tried to set input:defaultValue in the following manner <Field input={{ defaultValue: '${index + 1}' }}.... This attempt though changes initial value of the wrapped input component yet still, somehow, effects the state of the field, any changes in the field do not have any effect on the form state.

What am I missing?

1
You should use mapStateToProps and return your array of data under the initialValues property it is not common practice to attempt to set the initial values for your form in the render of your components. - Purgatory

1 Answers

0
votes

In order to set the initial state of values inside of a redux form, you need to provide initialValues property into redux form wrapped component:

//... your code above

export default compose(
  connect(state => ({
    // here, you are having an access to the global state object, so you can
    // provide all necessary data as the initial state of your form
    initialValues: {
      someField: "initial value"
    }
  })),
  reduxForm({
    form: "quizStepAddForm"
  })
)(QuizStepAddForm);