0

I have recently started working on react.js, while creating the login page I have used setstate method to set the value of userEmail to text box.

I have created a method which checks the validity of email address and I am calling it every time when user enters a new letter.

handleChangeInEmail(event) {
    var value = event.target.value;
    console.log("change in email value" + value);
    if(validateEmailAddress(value) == true) {
      this.setState(function() {
        return {
              showInvalidEmailError : false,
              userEmailForLogin: value,
           }
     });
   } else {
     this.setState(function() {
       return {
              showInvalidEmailError : true,
              userEmailForLogin: value

      }
  });
}

This method and userEmailForLogin state is passed in render method as

<EmailLoginPage  
   userEmailForLogin = {this.state.userEmailForLogin}
   onHandleChangeInEmail= {this.handleChangeInEmail}
/>

I am using the method to validate the email address and the method is

validateEmailAddress : function(emailForLogin) {
        if (/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(emailForLogin))  {  
            return true; 
        }  
        return false;  
    },

I am using this method and state in render of EmailLoginPage as <input type="text" name="" placeholder="Enter Email" className="email-input-txt" onChange={props.onHandleChangeInEmail} value = {props.userEmailForLogin}/>

This is working fine in normal case , but when I try to input a large email addess say yjgykgkykhhkuhkjhgkghjkhgkjhghjkghjghghkghbghbg@gmail.com, it crashes

IMO the frequent change in state is causing this but I couldn't understand what should be done to get rid of this.

Geek_To_Learn
  • 1,816
  • 5
  • 28
  • 48

3 Answers3

0

A solution using debounce. This way multiple setState can be reduced.

DEMO: https://jsfiddle.net/vedp/kp04015o/6/

class Email extends React.Component {
    constructor (props) {
        super(props)
        this.state = { email: "" }
    }

    handleChange = debounce((e) => {
        this.setState({ email: e.target.value })
    }, 1000)

    render() {
        return (
            <div className="widget">
                <p>{this.state.email}</p>
                <input onChange={this.handleChange} />
            </div>
        )
    }
}

React.render(<Email/>, document.getElementById('container'));


function debounce(callback, wait, context = this) {
  let timeout = null 
  let callbackArgs = null

  const later = () => callback.apply(context, callbackArgs)

  return function() {
    callbackArgs = arguments
    clearTimeout(timeout)
    timeout = setTimeout(later, wait)
  }
}
Ved
  • 11,837
  • 5
  • 42
  • 60
0

I think issue is with the regex only, i tried with other and it's working properly.

Instead of writing the if/else inside change function simply you are write it like this:

change(event) {
    var value = event.target.value;
    this.setState({
       showInvalidEmailError : this.validateEmailAddress(value),
       value: value,
    });
}

Copied the regex from this answer: How to validate email address in JavaScript?

Check the working solution:

class App extends React.Component {
    
    constructor(){
        super();
        this.state = {
            value: '',
            showInvalidEmailError: false
        }
        this.change = this.change.bind(this);
    }

    change(event) {
        var value = event.target.value;
        this.setState(function() {
            return {
                showInvalidEmailError : this.validateEmailAddress(value),
                value: value,
            }
        });
    }
    
    validateEmailAddress(emailForLogin) {
        var regex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
        if(regex.test(emailForLogin)){
          return true;
      }
        return false;
    }

    render() {
        return(
            <div>
                <input value={this.state.value} onChange={this.change}/>
                <br/>
                valid email: {this.state.showInvalidEmailError + ''}
            </div>
        );
    }
}

ReactDOM.render(
    <App/>, 
    document.getElementById("app")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<div id='app'/>
Mayank Shukla
  • 100,735
  • 18
  • 158
  • 142
  • But this approach will call setState for every changes. – Ved Jun 27 '17 at 11:26
  • is there any issue with this way of updating state? – Mayank Shukla Jun 27 '17 at 11:29
  • No. but caling setState for each change should be avoided. – Ved Jun 27 '17 at 11:30
  • @Ved can you attach any link for reference? i don't think debounce is required here. – Mayank Shukla Jun 27 '17 at 11:32
  • Forget about debounce. Just think about the use case. if an email is of 20 characters, than you are calling setState 20 times. – Ved Jun 27 '17 at 11:36
  • @Ved if calling `setState` for each input change should be avoided then react doc should have mentioned that somewhere, i am referring doc only: https://facebook.github.io/react/docs/forms.html#controlled-components i agree with your point but since setState is async and react diffing algo is highly optimised, i don't think it will make a big difference. – Mayank Shukla Jun 27 '17 at 11:38
  • You are going wrong way. have a look to this: https://medium.com/@justintulk/debouncing-reacts-controlled-textareas-w-redux-lodash-4383084ca090 – Ved Jun 27 '17 at 11:42
  • "Say you have a controlled – Ved Jun 27 '17 at 11:43
  • Well, finally I got the issue. you were correct the regex operation was heavy which causes hanging the app. changed the validation of email, its working fine now – Geek_To_Learn Jun 27 '17 at 11:46
  • @Ved i think you are little bit confused with the article see: **1.** for each input change state is getting updated check `handleChange` function of point 3 of article again. **2.** why he is suggesting to use `debounce`: *The problem is that every single character press fires off an action, which initiates an API request* --> to avoid making the api call for each change he is using debounce (**avoiding multiple api calls not multiple setState**) handleChange function: `handleChange = e => { const val = e.target.value this.setState({ value: val }, () => { ...... }` – Mayank Shukla Jun 27 '17 at 11:55
  • I have seen that. And that is one of the use cases. Anyway its all my approach to think and solve the problem. You have yours own. – Ved Jun 27 '17 at 12:03
  • @Ved i am not saying your answer is wrong, first time i heard this "avoiding setState for each input change" that why i asked for reference, btw thanks for telling this new thing :) – Mayank Shukla Jun 27 '17 at 12:13
0

You could use Lodash's debounce function so that the check function is not called unless the user stops typing for x amount of time (300ms in my scenario below).

_this.debounceCheck = debounce((value) => {
  if(validateEmailAddress(value)) {
    this.setState(function() {
      return {
        showInvalidEmailError : false,
        userEmailForLogin: value,
      }
    });
  } else {
    this.setState(function() {
      return {
        showInvalidEmailError : true,
        userEmailForLogin: value
      }
    });
  }
}, 300)

handleChangeInEmail(event) {
  _this.debounce(event.target.value)
}
Foxhoundn
  • 1,766
  • 2
  • 13
  • 19