1
votes

I've never used Formik before, but hearing positive things on the net it sounds like this is a great solution compared to rolling own...which I usually try to do.

The docs for Formik have a simple, single instance of some of their attributes, like initialValues and validationSchema. However, I need to make this reusable, since I have 2 versions of this in my app. So i want to pass in the fields as props, then create the initialValues as a form of state. This is OK, yes?

However, nothing renders...the value, errors params shown in the documents, always shows as undefined. Why is this? The state is updated and therefore, I assume the initialValues will update the values object. Scope inside the render method does not allow me to be able to use this.state.initial for example... Errors object remains undefined, but I thought it should at least exist?

Basically, I have a Parent component that is rending inner components, one of which is a form group. So I am passing the array of fields, and a schema object to the container of Formik component like so:

const newCompanyFields = ["name", "address", "revenue", "phone"];

<Card
  headerText="Create New Company"
  id="new-company"
  renderWith={() => (
    <React.Fragment>
      <div className="header">Add Company</div>
      <Forms fields={newCompanyFields} schema={NewCompanySchema} />
    </React.Fragment>
  )}
/>

Then, inside the <Forms> component, we will create instance of Formik like so:

class Forms extends Component {
  state = {
    initial: {}
  };

  componentDidMount() {
    //  we need to get the list of fields and setState to be used by Formik below
    if (this.props.fields) {
      let initialItems = {};
      this.props.fields.forEach(item => {
        return (initialItems[item] = "");
      });

      this.setState({ initial: initialItems });
    }
  }

  render() {
    return (
      <StyledForms>
        <Formik
      initialValues={this.state.initial}
      validationSchema={this.props.schema}
      onSubmit={values => {
        console.log(values, " submitted");
      }}
      render={({
        values,
        errors,
        touched,
        handleBlur,
        handleChange,
        handleSubmit,
        isSubmitting
      }) => (
        <Form>
          <FieldArray
            name="field-company"
            render={() => (
              <div>
                {values &&
                  Object.keys(values).map((field, index) => (
                    <div key={index}>
                      <Field name={field} />
                      {errors[field] && touched[field] ? (
                        <div>{errors[field]}</div>
                      ) : null}
                    </div>
                  ))}
                <button type="submit" disabled={isSubmitting}>
                  Submit
                </button>
              </div>
            )}
          />
        </Form>
      )}
    />
   </StyledForms>
    );
  }

Link to Console screenshot: https://screencast.com/t/Pt7YOxU1Oq57

Thank you for clarification.

UPDATE If I update my initialValues attribute to NOT rely on component state, it works.

// assume ComponentDidMount from above is removed now
const getInitialValues = passedFields => {
  let initialItems = {};
  passedFields.forEach(item => {
    return (initialItems[item] = "");
  });
  return initialItems;
};

<Formik
          initialValues={getInitialValues(this.props.fields)}
...
/>

Is this expected?

1
I think you need render prop in your Formik component as well.Colin Ricardo

1 Answers

0
votes

You need to use render() in your Formik component.

Something like:

<Formik render={({ values }) => {
  <Form>
    <FieldArray>
      ... use values
    </FieldArray>
  </Form>
}} />