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

Validation not working in functional component with hooks #97

Open
sachin8094 opened this issue Jun 7, 2019 · 26 comments
Open

Validation not working in functional component with hooks #97

sachin8094 opened this issue Jun 7, 2019 · 26 comments
Assignees

Comments

@sachin8094
Copy link

sachin8094 commented Jun 7, 2019

I am trying to use is in the functional component using React Hooks but its now showing up the validation message on UI

My React version is below
"react": "^16.8.6",
"react-dom": "^16.8.6",

Below is my code, Please check if I am missing anything.

import React from "react";
import SimpleReactValidator from "simple-react-validator";

export default function ExampleForm (){
  const [values, setValues] = React.useState({
    title: 'There was a server error the prevented the form from submitting.',
    email:'',
    review:''
  });

  const  handleChange = name => event => {
    setValues({ ...values, [name]: event.target.value });
  };

  const validator = new SimpleReactValidator({
    className: 'text-danger',
    messages: {
      email: 'That is not an email.',
    },
    validators: {
      ip: {
        message: 'The :attribute must be a valid IP address.',
        rule: function(val, params, validator) { 
          return validator.helpers.testRegex(val,/^(?!0)(?!.*\.$)((1?\d?\d|25[0-5]|2[0-4]\d)(\.|$)){4}$/i) && params.indexOf(val) === -1
        }
      }
    }
  });

  const submitForm= ()=> {
    if (validator.allValid()) {
      alert('You submitted the form and stuff!');
    } else {
      validator.showMessages();
    }
  }

  return (
    <div className="container">
      <div className="form-group">
        <label>Title</label>
        <input className="form-control" value={values.title}  onChange={handleChange("title")} />
 
        {/**********   This is where the magic happens     ***********/}
        {validator.message('title', values.title, 'required|alpha')}
 
      </div>
      <div className="form-group">
        <label>Email</label>
        <input className="form-control" value={values.email} onChange={handleChange("email")} />
 
        {/**********   This is where the magic happens     ***********/}
        {validator.message('email', values.email, 'required|email')}
 
      </div>
      <div className="form-group">
        <label>Review</label>
        <textarea className="form-control" value={values.review} onChange={handleChange("review")} />
 
        {/**********   This is where the magic happens     ***********/}
        {validator.message('review', values.review, 'required|min:20|max:120')}
 
      </div>
      <button className="btn btn-primary" onClick={submitForm.bind(this)}>Save Review</button>
    </div>
  );
}
@stuyam stuyam self-assigned this Jun 10, 2019
@stuyam
Copy link
Collaborator

stuyam commented Jun 10, 2019

@sachin8094 sorry to hear it isn't working for you within a functional component with hooks. I have never used either of those before. If anyone out there has any suggestions on how this works or if it works please chime in.

To me it all seems right. But you aren't doing this.forceUpdate() which may be done in a different way when using hooks, but Im guessing you may need to force rerender once you show the messages for the first time?

@josh-stevens
Copy link

@sachin8094 The problem is that you are creating a new validator instance on every render. You need to use some other pattern to make sure it's a singleton. The simplest workaround I came up with right off the bat is to store the validator in a state hook (don't need a setState since you don't want to change it).

const [validator] = React.useState(new SimpleReactValidator({...}))

It might make sense to useMemo or useEffect. I wasn't sure about useMemo though since the docs say "You may rely on useMemo as a performance optimization, not as a semantic guarantee." Maybe a useEffect would work? I haven't tried but maybe:

let validator; React.useEffect(() => { validator = new SimpleReactValidator({...}); }, []);

I don't know if this would work or when the effect would run, I can test that out later. But I know the first pattern with useState works.

@peyloride
Copy link

You can use useRef for this kind of job. https://stackoverflow.com/a/53146714/5160696

@santosgabriel
Copy link

@sachin8094 Did you try to validate your input in the onBlur event? It works for me.

  const [address, setAddress] = useState()
  const simpleValidator = useRef(new SimpleReactValidator())

  <Input
    name="name"
    value={companyInformation.name}
    onChange={handleInputChange}
    onBlur={simpleValidator.current.showMessageFor('name')} />
  {simpleValidator.current.message('name', companyInformation.name, 'required')}

@santosgabriel
Copy link

You can also use force update after a form submit to show all the messages at once:

const simpleValidator = useRef(new SimpleReactValidator())
const [, forceUpdate] = useState();

submitForm() => {
  const formValid = simpleValidator.current.allValid()
  if (!formValid) {
    simpleValidator.current.showMessages()
    forceUpdate(1)
  }
}

@stuyam
Copy link
Collaborator

stuyam commented Sep 19, 2019

If you can use forceUpdate with hooks like that then you can initialize the SRV with forceUpdate so it will do it automatically. something like this:

const [, forceUpdate] = useState()
const simpleValidator = useRef(new SimpleReactValidator({autoForceUpdate: {forceUpdate: forceUpdate}))

submitForm() => {
  const formValid = simpleValidator.current.allValid()
  if (!formValid) {
    simpleValidator.current.showMessages()
  }
}

@chaudharykiran
Copy link

chaudharykiran commented Apr 7, 2020

I have created one small hooks to solve this problem. It worked for me. Here is the repository: https://github.com/chaudharykiran/SimpleReactValidatorWithHooks

@stuyam
Copy link
Collaborator

stuyam commented Apr 7, 2020

I'm open to adding better support for hooks in SRV. If someone wants to open a PR or talk about some ideas I am open to it.

@chaudharykiran
Copy link

I want to work on that. cc. @stuyam

@stuyam
Copy link
Collaborator

stuyam commented Apr 8, 2020

@chaudharykiran great! I don't use hooks so someone else would be better suited for the main ideas of what this needs. What is SRV missing that would make it work easier with hooks?

@dinorhythms
Copy link

dinorhythms commented Apr 8, 2020

I have created one small hooks to solve this problem. It worked for me. Here is the repository: https://github.com/chaudharykiran/SimpleReactValidatorWithHooks

@chaudharykiran please how do I use showMessageFor on this?

@arifszn
Copy link

arifszn commented Apr 11, 2020

If you can use forceUpdate with hooks like that then you can initialize the SRV with forceUpdate so it will do it automatically. something like this:

const [, forceUpdate] = useState()
const simpleValidator = useRef(new SimpleReactValidator({autoForceUpdate: {forceUpdate: forceUpdate}))

submitForm() => {
  const formValid = simpleValidator.current.allValid()
  if (!formValid) {
    simpleValidator.current.showMessages()
  }
}

Does not show error messages after submitting form. Shows only when the form is submitted and then the input is changed.

Update: Working. Didn't call forceUpdate.

@h4r3en
Copy link

h4r3en commented Apr 17, 2020

In my case a hook's solution is working nice. Thanks @chaudharykiran

@chaudharykiran
Copy link

Awesome good job @h4r3en

@chaudharykiran
Copy link

please how do I use showMessage on this?

You have to pass true when you want to show error message.

if (notValid) {
   showMessage(true)
}

cc. @dinorhythms

@garethfentimen
Copy link
Contributor

If you can use forceUpdate with hooks like that then you can initialize the SRV with forceUpdate so it will do it automatically. something like this:

const [, forceUpdate] = useState()
const simpleValidator = useRef(new SimpleReactValidator({autoForceUpdate: {forceUpdate: forceUpdate}))

submitForm() => {
  const formValid = simpleValidator.current.allValid()
  if (!formValid) {
    simpleValidator.current.showMessages()
  }
}

Does not show error messages after submitting form. Shows only when the form is submitted and then the input is changed.

Update: Working. Didn't call forceUpdate.

It does work if you wrap like this:

const useValidator = (customMessage = {}, customValidator = {}): [SimpleReactValidator, Function] => {
  const [, forceUpdate] = useState();

  const validator = useRef(new SimpleReactValidator({
    messages: customMessage, 
    validators: customValidator,
    autoForceUpdate: { forceUpdate: () => forceUpdate(1) }
  }));

  return [validator.current, forceUpdate];
}

@LucSouza
Copy link

1

save my day!

@farisabundev
Copy link

farisabundev commented Oct 8, 2020

I found my own solution without using autoForceUpdate by using focus, hope this will help:

State:

// SET FOCUS
    const [focusPhoneNumber, setFocusPhoneNumber] = useState(false);

Input:

<input
name="phoneNumber"
value={phoneNumber}
onChange={(e) => handlePhoneNumberChange(e)}
onFocus={(e) => setFocusPhoneNumber(true)}
onBlur={focusPhoneNumber ? simpleValidator.current.showMessageFor('phoneNumber') : () => {}}
placeholder="2984928xxx" type="number" />

{ simpleValidator.current.message('phoneNumber', phoneNumber, 'required|min:9|max:14|numeric') }

and on submit button:

let isValid = validator.current.allValid();

 if(!isValid) {
       setFocusPhoneNumber(true);
       validator.current.showMessages(true);
 } 

@wztech0192
Copy link
Contributor

Here is a more advance custom hook to force rerender the component. This hook will not recreate a SimpleReactValidator instance in each rerender, tiny performance boost~

//useSimpleReactValidator.js
export default function useSimpleReactValidator(passInOptions = {}) {
    const [{ options }, forceUpdate] = React.useReducer(({ options }) => ({ options }), {
        options: passInOptions,
    });
    const simpleValidator = React.useMemo(
        () =>
            new SimpleReactValidator(
                options.autoForceUpdate
                    ? {
                          ...options,
                          autoForceUpdate: {
                              forceUpdate,
                          },
                      }
                    : options
            ),
        [options]
    );
    return [simpleValidator, forceUpdate];
}

//example
const [validator, forceUpdate] = useSimpleReactValidator({...anyOtherOptions, autoForceUpdate: true});

@albahmed23
Copy link

I have created one small hooks to solve this problem. It worked for me. Here is the repository: https://github.com/chaudharykiran/SimpleReactValidatorWithHooks

This is perfect! Worked like a charm. Appreciate you for adding this.

@pooja-yadav-ctrl
Copy link

@sachin8094 Did you try to validate your input in the onBlur event? It works for me.

  const [address, setAddress] = useState()
  const simpleValidator = useRef(new SimpleReactValidator())

  <Input
    name="name"
    value={companyInformation.name}
    onChange={handleInputChange}
    onBlur={simpleValidator.current.showMessageFor('name')} />
  {simpleValidator.current.message('name', companyInformation.name, 'required')}

I try this but it's showing errors on page load not on onblur

@wztech0192
Copy link
Contributor

@sachin8094 Did you try to validate your input in the onBlur event? It works for me.

const [address, setAddress] = useState()

const simpleValidator = useRef(new SimpleReactValidator())

<Input

name="name"
value={companyInformation.name}
onChange={handleInputChange}
onBlur={simpleValidator.current.showMessageFor('name')} />

{simpleValidator.current.message('name', companyInformation.name, 'required')}

I try this but it's showing errors on page load not on onblur

you need to wrap onBlur={simpleValidator.current.showMessageFor('name')} /> with a function.

onBlur={()=>simpleValidator.current.showMessageFor('name')} />

@pooja-yadav-ctrl
Copy link

@sachin8094 Did you try to validate your input in the onBlur event? It works for me.

const [address, setAddress] = useState()

const simpleValidator = useRef(new SimpleReactValidator())

<Input

name="name"
value={companyInformation.name}
onChange={handleInputChange}
onBlur={simpleValidator.current.showMessageFor('name')} />

{simpleValidator.current.message('name', companyInformation.name, 'required')}

I try this but it's showing errors on page load not on onblur

you need to wrap onBlur={simpleValidator.current.showMessageFor('name')} /> with a function.

onBlur={()=>simpleValidator.current.showMessageFor('name')} />

thanks it's working but after filling the correct input message was still there

@shal0mdave
Copy link

I have created one small hooks to solve this problem. It worked for me. Here is the repository: https://github.com/chaudharykiran/SimpleReactValidatorWithHooks

Awesome! This works for me. 🚀

@chaudharykiran
Copy link

Good work @shal0mdave

@SavithaDhatchinamoorthy

This works!

const [, forceUpdate] = useReducer((x) => x + 1, 0);
const validator = useRef(
new SimpleReactValidator({ autoForceUpdate: { forceUpdate: forceUpdate }})
);

Usage:
onBlur={validator.current.showMessageFor('name')} />

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

No branches or pull requests