Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Next] Nested FieldArrays and setTouched not working together #378

Closed
rovansteen opened this issue Jan 25, 2018 · 22 comments
Closed

[Next] Nested FieldArrays and setTouched not working together #378

rovansteen opened this issue Jan 25, 2018 · 22 comments

Comments

@rovansteen
Copy link

rovansteen commented Jan 25, 2018

Bug, Feature, or Question?

Bug

Current Behavior

When you have nested FieldArrays, let's say the following path: questions.0.values.0.label where questions and values are the arrays and label is a regular input field. When onBlur is invoked on label, it wants to touch the path. But at that point (due to changing the values array) the current touched state is:

questions: 0: {form_question_type_id: true, values: true}

which causes the following error in setDeep:

Uncaught TypeError: Cannot create property '0' on boolean 'true'

which happens on the last line of this method:
https://github.com/jaredpalmer/formik/blob/next/src/utils.ts#L69

Desired Behavior

It should update the touched state to a deep nested state like this:

questions: 0: {form_question_type_id: true, values: { label: true }}

Suggested Solutions

I'm not sure I understand the current implementation of setDeep. Why not just clone the object and use something like https://github.com/kalmbach/bury?

Info

  • Formik Version: 0.11.0-beta.1
  • OS: Mac OS High Sierra
  • Node Version: v9.4.0
  • Package Manager and version: Yarn v1.3.2
@davidhenley
Copy link

davidhenley commented Jan 26, 2018

To add onto this:

values.array is showing as an array, while touched.array is showing as an object. Both should have the same structure.

<FieldArray
  name="products"
  render={mutators => (
    <div>
      {values.products.map((product, index) => (
        <Field name={`products.${index}`} key={index} label={`Product ${index + 1}`} component={Form.FormikProduct} />
      ))}
      <Button type="button" size="mini" color="blue" onClick={() => mutators.push('')}>
        Add
      </Button>
    </div>
  )}
/>

This comes straight from props.values and props.touched

"values": {
  "products": [
    "",
    ""
  ]
},
"touched": {
  "products": {
    "0": true,
    "1": true
  }
}

@jaredpalmer
Copy link
Owner

Damn wish I knew about bury earlier. Will have to check this out more detail

@timc13
Copy link
Contributor

timc13 commented Jan 26, 2018

why not stick with lodash and use set which works for bracket notation AND dot notation...

@timc13
Copy link
Contributor

timc13 commented Jan 26, 2018

i believe this is a result of using dot notation to access array items.

i am using bracket notation, and the array field gets completely replaced with a boolean.
See #362

@timc13
Copy link
Contributor

timc13 commented Jan 29, 2018

awww, what was wrong with my PR? =X

@jaredpalmer
Copy link
Owner

Awww I'm sorry man. I was on a grind and just missed this. Agh.

jaredpalmer added a commit that referenced this issue Feb 1, 2018
* Close #200. Add Field level validation

* Regenerate doctoc

* Remove handleChangeValue completely

* v0.11.0-alpha.1

* Add missing name in call to setFieldError (#290)

* v0.11.0-alpha.2

* #281 Add array helpers MVP, update ts setup for better DX (#298)

* v0.11.0-alpha.3

* Actually export FieldArray

* v0.11.0-alpha.4

* Upgrade to React 16, TypeScript 2.6.2 (#300)

* #283 Update to React 16, TypeScript 2.6.2

* Update Travis build to use npm instead of yarn

* Move back to yarn (#301)

* Fix travis script to use yarn.

* Add form to TS types of FieldArray

* v0.11.0-beta.1

* Close #281 #155. Add docs about array helpers

* Add field array examples and update toc

* Fix unshift in FieldArray (#337)

* Fix FieldArray component prop (#341)

* Fix FieldArray component prop

* nit: test names [skip ci]

* Allow bracket path support (#334)

* feat(Field): Allow bracket path support

* chore(deps): Add types for lodash.topath

* chore(deps): yarn.lock

* Fix #280. Fix the definition dirty and isValid (#365)

* Fix #280. Change the meaning of dirty and isValid

* Lock in to path dep

* Update tests for new definition of dirty

* Update docs

* Fixed typo in FieldArray code (#380)

* Fix setDeep mutations with nested values (#373)

* Fix start task

* Get rid of mutators file

* v0.11.0-rc.1

* #285 #122 Add example multistep / form wizard

* #378 remove setFieldTouched from FieldArray

* v0.11.0-rc.2

* #223 Fix checkbox validation error (again)

* Add onReset callback. (#328)

* feat(formik): Add onReset callback.

* feat(formik): Use promise sniffing for onReset prop.

This passes returned promise data to resetForm, allowing the promise to add new nextValues.
@a-tarasyuk
Copy link
Contributor

a-tarasyuk commented Feb 1, 2018

@jaredpalmer Hi, thank you for new component ArrayField.

I noticed that torched and errors has a different data structure and after remove an item from values in touched property is not deleted. There is commit where was removed setFieldTouched.

"values": {
    "email": "",
    "firstName": "",
    "lastName": "",
    "emails": [
      "test",
      ""
    ]
  },
  "errors": {
    "firstName": "First name is required.",
    "lastName": "Last name is required.",
    "emails": [
      null,
      "emails[1] is a required field"
    ],
    "email": "Email is required!"
},
"touched": {
    "email": true,
    "firstName": true,
    "lastName": true,
    "emails": {
      "0": true,
      "1": true,
      "2": true
    }
}

There are two elements in values and errors and three in touched. Fiddle

  1. Add several items
  2. Click Submit
  3. Remove one of them
  4. Add new item

You will see that new item has an error due to touched.
-> changeValue -> setFieldValue -> runValidations in each of these methods, there is no logic which will update touched in the case when an item was removed

@jaredpalmer Is there any way to avoid this? Thanks in advance.

@jaredpalmer
Copy link
Owner

Working on a fix now.

@davidhenley
Copy link

davidhenley commented Feb 1, 2018

I can't wait to merge my form branch into master!!

@jaredpalmer
Copy link
Owner

I figured it out! Will open a PR

jaredpalmer added a commit that referenced this issue Feb 1, 2018
* #378 Alter touched and error if FA.remove and FA.pop

* Put back setNestedObjectValues as it was
@eferrervaughn
Copy link

eferrervaughn commented May 8, 2018

@jaredpalmer I still get the error, when I leave the text box. Do I have to do something in code to dodge this error:

formik.es6.js:5556 Uncaught TypeError: Cannot create property '0' on boolean 'true'

Here is a code snippet:

<FieldArray
      name="friends"
      render={arrayHelpers => (
        <Form>
          <h3>Name</h3>
          <input
            name="name"
            type="text"
            value={values.name}
            onChange={handleChange}
            onBlur={handleBlur}
          />
          {values.friends && values.friends.length > 0 ? (
            values.friends.map((friend, index) => (
              <div>
                <MySelect
                  name={`friends.${index}.type`}
                  value={`friends.${index}.type`}
                  onChange={setFieldValue}
                  current={{ curVal: values.friends, index }}
                  options={[
                    { value: "single_text", label: "Single Line Text" },
                    { value: "multi_select", label: "Multi Select" }
                  ]}
                  onBlur={setFieldTouched}
                />

                {
                  values.friends[index] &&
                  <Field name={`friends.${index}.name`} />

It happens in the Field Component

@sadi304
Copy link

sadi304 commented Feb 25, 2019

@eferrervaughn any update? this issue is still occurring

@eferrervaughn
Copy link

It's good for me feel free to close

@sadi304
Copy link

sadi304 commented Feb 25, 2019

@eferrervaughn no, what i meant is that this is also happening in my code, did you solve it? :p

@johncmunson
Copy link

johncmunson commented May 29, 2019

I'm also running into this error. Any updates? Should this issue be reopened?

@slightlytyler
Copy link
Collaborator

Running into this as well on v1 and v2 and I'm noting using a FieldArray, just an array value. Thinking this unsolved @jaredpalmer

@slightlytyler
Copy link
Collaborator

Not sure how to handle this situation tbh, maybe just encourage using resetForm in these cases rather than setFieldValue? The error that occurs is useless atm

@slightlytyler
Copy link
Collaborator

For those looking for a solution I was doing something like:

// base on some condition I either
setFieldValue('text', '');
// or
setFieldValue('text', { default: '', keys: [] });

I was changing the shape of a field value which was causing the touched functionality to bork. After filling out text as a string and toggling it to the the object shaped formik's values and touched shape no longer match.

The solution is to use resetForm instead

resetForm(prevValues => ({ ...prevValues, text: '' });
// or
resetForm(prevValues => ({ ...prevValues, text: { default: '', keys: [] } });

@jaredpalmer
Copy link
Owner

@slightlytyler yes. Formik assumes that you keep you shape identical with respect to the path names. So in a nested situation if you go from a array to a value touched behavior could be off. you should indeed reset the form.

@jaredpalmer
Copy link
Owner

I don’t think this is a bug, but it is a contract that we should document more explicitly.

@elvisguillen
Copy link

I was getting this error as well when using a custom slider input with setFieldValue and then typing and click out of a seperate empty Field or FastField component. Seems like the touched values were definitely off between array and value.

I fixed the issue and eliminated the error on my end by using a custom input component instead of Formik's Field or FastField components alongside my custom slider.

Old Input:

<FastField
  name={`result[${index}].value[0]`}
  className='form-control'
  onChange={handleChange}
/>

New Input:

<input
  name={`result[${index}].value[0]`}
  className='form-control'
  onChange={handleChange}
  type='text'
  value={values.result[index].value[0]}
/>

@arvinsim
Copy link

@elvisguillen So is your structure an array of array items? Trying to understand why you put value[0].

@jaredpalmer Is the documentation updated? Trying to find out how touched is being handled for an array of objects.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests