Nesting redux form fields for grouped validation

1.3k Views Asked by At

Is the below considered a valid use of redux-form Fields?

const ValidatedFieldGroup = (props) => {
  const {meta: {touched, error}} = props

  return (
    <div className={touched && error ? 'has-error' : ''}>
      <Field name="one" .... />
      <Field name="two" .... />
    </div>
  )
}

const MyMainComponent = (props) => {
  return <Field name="twofields"
                component={ValidatedFieldGroup}
                validate={myValidator} .... />
}

const myValidator = (value, allValues) => {
  return (
    allValues.one === "pretend-this-is-valid" && allValues.two === "pretend-this-is-valid"
  ) ? undefined : 'One of the fields is invalid - sort it out!'
}

A 'valueless' parent Field is implemented solely to hook into the sync field level validation pipeline. The props of this component can then be used to alter the UI / state of it's children (which in turn contain some RF Fields implementing the actual form values)

Real-world example = assume there are a group of five check boxes... if at least TWO of them are not checked then they should ALL be wrapped in a 'red bordered' div.

In seems to work thus far, but I am conscious that there may be an easier / better / correct way to achieve the same result or that I could in fact be setting myself up for future troubles!!

Thanks in advance.

1

There are 1 best solutions below

4
On

Although this work-around results in the desired UI (namely, a single div with the correct class), you'll end up with three fields in the redux-form store, one, two, and twofields, which seems undesirable. Presumably, you'll never do anything with the field twofields on the backend, since it's only used for presentation. This goes against the idea that the redux-form store should map to the fields in your backend (DB, or whatever...).

You could instead use the Fields component, so that you only register the one and two fields, which is more consistent:

import { Fields, ...} from "redux-form";

const renderValidatedFields = fields => {
    const { one, two } = fields;
    const showError = (one.meta.touched && one.meta.error) || (two.meta.touched && two.meta.error);
    return ( 
       <div className={showError ? 'has-error' : ''}>
          <input {...one.input} type="checkbox" />
          <input {...two.input} type="checkbox" />
       </div>
    )
}        

export default MyMainFieldComponent = props => {
    return <Fields names={["one", "two"]} component={renderValidatedFields} />
}

Then put your validator against redux-form in the config:

import React from "react";
import { reduxForm } from "redux-form";

import MyMainFieldComponent from "./MyMainFieldComponent";

// validate the whole form
const myValidator = values => {
    const msg = "Invalid selection.";
    const errors = {};

    if (!values.one) {
        errors.one = msg;
    }

    if (!values.two) {
        errors.two = msg;
    }

    return errors;
}

...

let MyForm = props => {
    ...
    return (
        <form ...>
            <MyMainFieldComponent />
        </form>
    )
}

MyForm = reduxForm({
    ...,
    validate: myValidator
})(MyForm);

export default MyForm;