redux-form: Cannot use a higher-order function in Field Validation

Are you submitting a bug report or a feature request?

Bug

What is the current behavior?

When using field level validators, I am unable to use a higher order function to produce the validation function

This works:

const required = value => (value ? undefined : 'Required')

<Field
  label="Email"
  name="email"
  component={InputField}
  validate={required}
  placeholder="email"
  type="text"
/>

This DOES NOT work:

const required = (msg = 'Default Message') => value => (value ? undefined : msg)

<Field
  label="Email"
  name="email"
  component={InputField}
  validate={required('Message Override')}
  placeholder="email"
  type="text"
/>

What is the expected behavior?

I would expect to pass into the field validator a returned function that matches the validator interface even if it was produced by a higher order function

Sandbox Link

https://codesandbox.io/s/G6ARklAMr

What’s your environment?

Redux Form: 7.0.3 OS: macOS Browser: Chrome 60

Other information

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 9
  • Comments: 24 (4 by maintainers)

Commits related to this issue

Most upvoted comments

I’m more than just “an issues helper”. 😄 Two things:

  1. Okay, this is silly. The form doesn’t need to be reregistered, the form component just needs to be notified of a new validation function.

  2. That said, and perhaps I should write a medium post about this…

Creating a new function for a prop in render() is an anti-pattern.

It [almost always] requires that the child rerender itself, when require('This field is required') always generates the exact same function. Memoizing can get you some benefits here, for sure, as would @danielrob’s other example of saving them as this.validateUsername-type members.

It would be wonderful if you could do validate={require('Error message')}, because it looks so beautiful, but that’s not the way React works at the moment.

Hope that helps.

@gabrielhpugliese ok the latest: You must define your validation functions outside of your render method. Additionally if they are a higher order function which receive props you should instantiate them when the component mounts / on instantiation.

So either (option 1):

const myvalidate = (validateParams) => ( /* some function on validateParams */)
const MyComponent = () => {
  <Field
    validate={myvalidate}
  />
}

Or something like (option 2):

const myValidate = config => (validateParams) => ( /* some function of validateParams & config */ )

class MyComponent extends React.Component {
  constructor(props) {
    super(props)
    this.validate = myValidate(props)
  }

  render() {
    <Field
       validate={this.validate}
    >
  }
}

Or (option 3)

import { memoize } from 'lodash'

const myvalidate = memoize(config => (validateParams) => ( /* some function on validateParams & config */))
const MyComponent = (props) => {
  <Field
    validate={myvalidate(props)}
  />
}

Please note I’m just doing this off the top of my head, and I’m primarily an issues helper and haven’t tried this on my personal project yet, so this is just my understanding presently.

Any plans on fixing this or reverting to 6.8? Maybe I’m missing something, but is there any reason to enforce specific usage pattern on users instead of letting them decide and tackle their own performance problems (if they even exist at all, @gabrielhpugliese got a point here)?

@gabrielhpugliese right so tl;dr the new API as it stands means that you must optimise, you can’t generate a new function every render - you must either bind during lifecycle methods/instantiation or use memoization if you have a validator HOF.