final-form / react-final-form

🏁 High performance subscription-based form state management for React
https://final-form.org/react
MIT License
7.38k stars 480 forks source link

Passing additional data into <Field /> which will be accessible in validate #669

Open marrkeri opened 4 years ago

marrkeri commented 4 years ago

I need to pass additional object myCustomData which I can access in meta.data <Field name="firstName" myCustomData={{isOk: true}} validate={(value, values, meta) =>{ return meta.data.myCustomData.isOk ? null : 'Error' }} />

Passing additional data into Field which I can access in 'validate ' function would be very helpful

Originally posted by @lock in https://github.com/final-form/react-final-form/issues/355#issuecomment-544117342

jsrhodes15 commented 4 years ago

Just an avid consumer of this library, so author/maintainers feel free to correct me, but my advice would be to use a curried function here. We do this all the time:

const customIsValid = (data) => (value) => data.isOk ? undefined : 'Error';

<Field 
  name="too"
  validate={customIsValid({isOk: true})
 >

With this pattern you have/can have access to whatever values you need.

msageryd commented 4 years ago

I don't know how to do this in JSX. But if you first register your fields manually you can populate the data field which will be accessible as meta.data.

  const unregisterField = form.registerField(
    'username',
    fieldState => {
      //subscription stuff goes here if you want
      const {blur, change, focus, value, touched, valid, ...rest} = fieldState;
    },
    {
      // subscription triggers if you need any
      // active: true,
      // dirty: true,
      // touched: true,
      // valid: true,
      // value: true,
      // data: true,
    },
    {
      data: {specialData: 'this string will be accessible as meta.data.specialData'},
      initialValue: 'You can also initialise the field here',
    }
  );

The function signature for registerField isn't very flexible. I think the below is the very minimum you need in the call in order to set your meta.data.

  const unregisterField = form.registerField(
    'username',
    () => null,  //must provide a dummy function
    {},          //must provide an empty object
    {
      data:  {specialData: 'this string will be accessible as meta.data.specialData'},
    }
  );
jsrhodes15 commented 4 years ago

Just an avid consumer of this library, so author/maintainers feel free to correct me, but my advice would be to use a curried function here. We do this all the time:

const customIsValid = (data) => (value) => data.isOk ? undefined : 'Error';

<Field 
  name="too"
  validate={customValidate({isOk: true})
 >

With this pattern you have/can have access to whatever values you need.

Would really like to understand why this was given a "thumbs down"

marrkeri commented 4 years ago

@msageryd even in JSX this won't help, because usually you want to react on some changes in your state and your approach will give me option pass data only during registration.

@jsrhodes15 I would say that in the most cases your approach is ok - only downside is that function is recreated during every rerender. Not big deal if you do memoization on right places.

I think that additional data passed as I describe is the best solution but I'm using your approach right now because it works ! ;)

msageryd commented 4 years ago

@marrkeri You didn't write that you wanted to update the data, so I didn't answer that question. The code I wrote above is just the initialisation part. If you want to update meta.data you can do this with a mutator. Or even install one of Erik's companion libs for this (it's actually just one function). https://www.npmjs.com/package/final-form-set-field-data

One of Erik's blog posts describes this. The second part (about field warnings) might apply to your use case: https://blog.cloudboost.io/final-form-decorators-calculated-fields-and-warnings-bf9cccd21b7e