5

I want to add validation to the input fields. I am using react hook form. The validation should be like the sum of the fields should be 100. If any of the fields make the sum more or lesser than 100, it should show an error at the input field (the last edited one).

sandbox url: https://codesandbox.io/s/distracted-elion-z2wob?file=/src/App.js

thanks

Abidh
  • 457
  • 2
  • 5
  • 13
  • thanks for the sandbox but you where is your attempt in handleSubmit? can you use ```const handleSubmit = (form) => { console.log('form', form.questions) }``` to see the values, you can add them up then display error if more or less than 100. – Someone Special Nov 27 '20 at 18:09

4 Answers4

5

There's a recent new feature in react-hook-form v7.34.0 that provides this kind of validation out of the box...

You set it up when you define the field array

In your case, you could run the check for sum of fields == 100 inside a custom validation function

useFieldArray({
  name: 'test',
  rules: {
    validate: (fieldArrayValues) => {
      // check that sum of all fields == 100
    },
  }
})

And then you check for/use the error as such

errors?.test?.root?.message

See here for further details...

https://react-hook-form.com/api/usefieldarray/

https://github.com/react-hook-form/react-hook-form/issues/6879

https://github.com/react-hook-form/react-hook-form/pull/8562

Danoz
  • 977
  • 13
  • 24
0

why don't you use ranges ?? you can set min to 0 and max 100-current, this would prevent user to set any value above 100. About values under a 100 you could check manually.


<input type="range" min="0" max={100-currentTotal} step={1}/>
{currentTotal< 100 && lastEdited? "error the sum should be equal to 100" : null}
// I used 1 as the step, but you can set any value


0

this handleSubmit function will get all your fields, get your values, add them and provide you with the total. You can handle the error in the if-else statement.

 const handleOnSubmit = (form) => {
       console.log('form fields', form.questions)
       let data = form.questions.map( x => x.percentage ).reduce((a,b) => a + b);

      if ( data !== 100) {
        console.log('total', data , ' is not 100');
        //error handling here.
      }
       
  };

sandbox

react-use-form error handling <- Error handling codes and example here.

Someone Special
  • 12,479
  • 7
  • 45
  • 76
0

You just need to use Yup's test() method to validate the total:

resolver: yupResolver(
  object().shape({
    questions: array()
      .of(
        object().shape({
          percentage: number().typeError('Wrong type.').required('Required.')
        })
      )
      .required()
      .test(
        'sum',
        'The total should be less or equal than 100.',
        (questions = []) => {
          const total = questions.reduce((total, question) => {
            return total + (question.percentage || 0);
          }, 0);

          return total <= 100;
        }
      )
  })
),

If that validation fails, the errors object will look like this:

{
  "questions": {
    "message": "The total should be less or equal than 100.",
    "type": "sum"
  }
}

You can then display the error with { errors.questions?.message }.

Danziger
  • 19,628
  • 4
  • 53
  • 83
  • https://codesandbox.io/s/lingering-shadow-0cxxo?file=/src/App.js the same approach, but the errors are not showing properly.. can you have a look? – Abidh Nov 30 '20 at 08:57
  • It looks like the validation is actually running and removing the error, but `react-hook-form`'s `error` object is not. I suspect this has to do with render isolation, but somehow it's not properly checking for updates on Yup's errors object. Maybe you should open an issue on `react-hook-form`. – Danziger Dec 03 '20 at 19:05