onClick does not work after removing "disabled" from (React) button - javascript

const MIN_LENGTH = 6;
class Login extends React.Component {
constructor() {
super();
this.state = {
email: '',
password: '',
emailValid: false,
passwordValid: false,
// redirect: false,
};
this.handleEmail = this.handleEmail.bind(this);
this.handlePassword = this.handlePassword.bind(this);
this.handleClick = this.handleClick.bind(this);
}
componentDidUpdate() {
this.handleButton();
}
handleEmail(ev) {
const email = ev.target.value;
const re = /^\w+([-+.']\w+)*#\w+([-.]\w+)*\.\w+([-.]\w+)*$/;
const isValid = re.test(email);
if (isValid) {
this.setState({ emailValid: true, email });
} else {
this.setState({ emailValid: false, email: '' });
}
}
handlePassword(ev) {
const size = ev.target.value.length;
if (size >= MIN_LENGTH) {
this.setState({ passwordValid: true, password: ev.target.value });
} else {
this.setState({ passwordValid: false, password: '' });
}
}
handleButton() {
const { emailValid, passwordValid } = this.state;
const btn = document.getElementById('btnLogin');
if (emailValid && passwordValid) {
btn.disabled = false;
} else {
btn.disabled = true;
}
}
handleClick(ev) {
console.log(ev);
ev.preventDefault();
console.log('click!');
}
render() {
const { email, password, emailValid, passwordValid } = this.state;
if (emailValid && passwordValid) {
console.log(`Email:${email}\nSenha:${password}`);
}
return (
<section>
<label htmlFor="email-input">
Email
<input type="email" data-testid="email-input" onChange={ this.handleEmail } />
</label>
<br />
<label htmlFor="password-input">
Password
<input
type="password"
data-testid="password-input"
onChange={ this.handlePassword }
/>
</label>
<br />
<button
type="button"
id="btnLogin"
onClick={ this.handleClick }
disabled
>
Login
</button>
</section>
);
}
}
export default Login;
The code above is a React component that will login my application. It is important to say that the validations are working perfectly, the button is only enabled when everything goes well, however when clicking the button nothing happens.The function "handleEmail" has the role of verifying via regex the validity of the email, and the email is only changed in the component state when it is valid. The "handlePassword" function only checks if the password entered is greater than or equal to 6. I'm storing this information in the component's state but the idea is to save this data in a global state in the future using "Redux". My only problem for now is the bug with the button's onClick.

You should not access the dom elements from the React directly, because react controls each component using js and does not see that you have changed the property in the html. You shouldn't do things like this one:
handleButton() {
const { emailValid, passwordValid } = this.state;
const btn = document.getElementById('btnLogin');
if (emailValid && passwordValid) {
btn.disabled = false;
} else {
btn.disabled = true;
}
}
You should add the property to the state, that stores the status of the button. Aka, add disabled boolean property and change it in the handler.
Your button gonna look like:
<button ... disabled={this.state.buttonDisabled} />
Your handleButton gonna look like this:
handleButton() {
const { emailValid, passwordValid } = this.state;
this.setState({
buttonDisabled: !(emailValid && passwordValid)
});
}

Related

How to avoid input value reset in Reactjs?

I am referring to this tutorial for simple react autocomplete https://www.digitalocean.com/community/tutorials/react-react-autocomplete
But I have a slightly different requirement. Instead of typing something on the input field, I want all the suggestions to come up on clicking the input field. I am basically implementing a requirement where on clicking the input field, it should show user what are the available options.
Here is my sandbox https://codesandbox.io/s/distracted-easley-wdm5x
Specifically in the Autocomplete.jsx file (as mentioned below)
import React, { Component, Fragment } from "react";
import PropTypes from "prop-types";
class Autocomplete extends Component {
static propTypes = {
suggestions: PropTypes.instanceOf(Array)
};
static defaultProps = {
suggestions: []
};
constructor(props) {
super(props);
this.state = {
// The active selection's index
activeSuggestion: 0,
// The suggestions that match the user's input
filteredSuggestions: [],
// Whether or not the suggestion list is shown
showSuggestions: false,
// What the user has entered
userInput: ""
};
}
onChange = (e) => {
const { suggestions } = this.props;
const userInput = e.currentTarget.value;
// Filter our suggestions that don't contain the user's input
const filteredSuggestions = suggestions.filter(
(suggestion) =>
suggestion.toLowerCase().indexOf(userInput.toLowerCase()) > -1
);
this.setState({
activeSuggestion: 0,
filteredSuggestions,
showSuggestions: true,
userInput: e.currentTarget.value
});
};
onClick = (e) => {
this.setState({
activeSuggestion: 0,
filteredSuggestions: [],
showSuggestions: false,
userInput: e.currentTarget.innerText
});
};
onClick2 = (e) => {
console.log("text check", e.currentTarget.innerText);
if (e.currentTarget.innerText === "") {
const { suggestions } = this.props;
const filteredSuggestions = suggestions;
this.setState({
activeSuggestion: 0,
filteredSuggestions,
showSuggestions: true,
userInput: e.currentTarget.innerText
});
}
};
onKeyDown = (e) => {
const { activeSuggestion, filteredSuggestions } = this.state;
// User pressed the enter key
if (e.keyCode === 13) {
this.setState({
activeSuggestion: 0,
showSuggestions: false,
userInput: filteredSuggestions[activeSuggestion]
});
}
// User pressed the up arrow
else if (e.keyCode === 38) {
if (activeSuggestion === 0) {
return;
}
this.setState({ activeSuggestion: activeSuggestion - 1 });
}
// User pressed the down arrow
else if (e.keyCode === 40) {
if (activeSuggestion - 1 === filteredSuggestions.length) {
return;
}
this.setState({ activeSuggestion: activeSuggestion + 1 });
}
};
render() {
const {
onChange,
onClick2,
onClick,
onKeyDown,
state: {
activeSuggestion,
filteredSuggestions,
showSuggestions,
userInput
}
} = this;
let suggestionsListComponent;
if (showSuggestions) {
if (filteredSuggestions.length) {
suggestionsListComponent = (
<ul className="suggestions">
{filteredSuggestions.map((suggestion, index) => {
let className;
// Flag the active suggestion with a class
if (index === activeSuggestion) {
className = "suggestion-active";
}
return (
<li className={className} key={suggestion} onClick={onClick}>
{suggestion}
</li>
);
})}
</ul>
);
} else {
suggestionsListComponent = (
<div className="no-suggestions">
<em>No suggestions, you're on your own!</em>
</div>
);
}
}
return (
<Fragment>
<input
type="text"
onChange={onChange}
onKeyDown={onKeyDown}
value={userInput}
onClick={onClick2}
/>
{suggestionsListComponent}
</Fragment>
);
}
}
export default Autocomplete;
In the input element in return section,
<input
type="text"
onChange={onChange}
onKeyDown={onKeyDown}
value={userInput}
onClick={onClick2}
/>
I have added a onClick functionality that calls the function onClick2.
onClick2 = (e) => {
console.log("text check", e.currentTarget.innerText);
if (e.currentTarget.innerText === "") {
const { suggestions } = this.props;
const filteredSuggestions = suggestions;
this.setState({
activeSuggestion: 0,
filteredSuggestions,
showSuggestions: true,
userInput: e.currentTarget.innerText
});
}
};
My function simply returns all the suggestions back on clicking the input field. I am able to select items from suggestions and it gets put in the input field. But when I click on the input field again, the value disappears.
I want this autocomplete suggestion to only show once on clicking the empty input field and after selecting the item from list, I should be able to edit the value further.
What am I doing wrong?
Input values are not stored into innerText, but in value prop.
Look at this:
onClick2 = (e) => {
console.log("text check", e.currentTarget.innerText);
if (e.currentTarget.value === "") {
const { suggestions } = this.props;
const filteredSuggestions = suggestions;
this.setState({
activeSuggestion: 0,
filteredSuggestions,
showSuggestions: true,
userInput: e.currentTarget.value
});
}
};
This should solve your problem
You are doing the wrong check in onClick2 function. Instead of checking e.currentTarget.innerText === "", it has to be e.target.value as we are validating the text in input field.
onClick2 = (e) => {
console.log("text check", e.target.value);
if (e.target.value === "") {
const { suggestions } = this.props;
const filteredSuggestions = suggestions;
this.setState({
activeSuggestion: 0,
filteredSuggestions,
showSuggestions: true,
userInput: e.currentTarget.innerText
});
}
};
Here is the working link - https://codesandbox.io/s/jolly-meadow-5fr6x?file=/src/Autocomplete.jsx:1344-1711

function works only in debbuger React

I'm pretty new with React so I need your help.
I have a component with multiple inputs, so i have to validate them.
Submit button is disabled by default, and only if the inputs are not blank, I make it able to Submit.
If I delete the value inside the input, button should go back to disabled.
My problem is, this function(validate function) works only in debugger when I go step by step.
Can someone help me?
Here are segments of my code that I find useful to understand my problem.
this.state = {
profile: newProfile,
disable: true,
};
let newProfile= {
firstName: "",
lastName: "",
nickname: "",
email: ""
};
validate = () => {
console.log(this.state)
debugger;
if (!this.state.profile.name || !this.state.profile.email) {
return false;
} else {
console.log("Profile name and email NOT BLANK")
console.log(this.state)
return true;
}
};
profileChange= ((target, value) => {
this.setState(prevState => {
let profile= this.state.profile;
profile[target] = value;
return {
profile: profile,
}
})
const isValid = this.validate();
if (isValid) {
console.log("valid inputs");
this.setState({disable: false});
}
else{
console.log("invalid inputs");
this.setState({disable: true});
}
});
setState is an asynchronous function (Why?) and at the point where you are calling this.validate, this.state.profile hasn't been set yet.
But when you are walk through the execution step by step, the state is being updated to the value you want and hence it is working for you.
The solution here is to use the callback function that setState provides to execute validate only after the state is set.
profileChange = (target, value) => {
this.setState(prevState => {
return {
profile: {
...prevState.profile,
[target]: value,
}
};
}, () => {
const isValid = this.validate();
if (isValid) {
console.log("valid inputs");
this.setState({ disable: false });
} else {
console.log("invalid inputs");
this.setState({ disable: true });
}
});
};
Also note that I have used prevState inside the setState instead of this.state, so that the profile state is not actually mutated.
The setState function is asynchronous, which means that wile the state is being updated, other functions could be fired.
What I think is happening in your case, is that the state is being updated, but before that happened, this.validate() is already called.
To fix this, you have to add the code you would like to fire after you updated that state, as callback:
this.setState(prevState => {
let profile= this.state.profile;
profile[target] = value;
return {
profile: profile,
}
}, () => {
const isValid = this.validate();
if (isValid) {
console.log("valid inputs");
this.setState({disable: false});
} else {
console.log("invalid inputs");
this.setState({disable: true});
}
});
you can use "disabled" params on input here's an example
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" disabled={!this.state.value}/>
</form>
);
}
}
and here's a codepen i made for you to test faster ragnar

ReactJS form validation

I have simple form validation for react. I made this from this source
http://www.dotnetawesome.com/2016/02/create-simple-forms-with-validation-in-react-js.html
there is my code. when I submit the form input error occurred inside of the isValid function. please help me to fix this.
AddMember page
class AddMember extends Component {
constructor(props) {
super(props);
this.state = {
loading : true,
formData: {
fname : "",
lname : "",
address1: "",
address2: "",
city : "",
mobile : "",
role : "",
email : "",
gender : ""
},
fields : []
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
this.register = this.register.bind(this);
}
componentDidMount() {
document.title = globals.title + "Add New Member";
setTimeout(() => {
this.setState({loading: false})
}, 500);
}
handleSubmit = (e) => {
e.preventDefault();
let validForm = true;
this.state.fields.forEach(function (field) {
console.log(field.refs[field.props.name]);
if (typeof field.isValid === "function") {
let validField = field.isValid(field.refs[field.props.name]);
validForm = validForm && validField
}
});
if (validForm) {
console.log(this.state.fields);
}
};
handleChange = (name) => (value) => {
let formData = {
[name]: value
};
this.setState({
formData: formData
})
};
register (field) {
let s = this.state.fields;
s.push(field);
this.setState({fields: s});
console.log("ss");
};
render() {
const {classes} = this.props;
if (this.state.loading) {
return (
<div className={classes.posRelative}>
<CircularProgress size={80} className={classes.progress}/>
</div>
)
}
return (
<Grow in={true} style={{transformOrigin: "0 0 0"}}>
<Paper className={classes.root}>
<form
onSubmit={this.handleSubmit}
noValidate>
<Grid container spacing={16}>
<Grid item xs={12} sm={6}>
<MuiValidator placeholder={"First Name"} name={"fname"} type={"email"}
onChange={this.handleChange("fname")} value={this.state.formData.fname}
inputProps={{required:true}} onComponentMounted={this.register} fullWidth={true}/>
</Grid>
</Grid>
<Button type={"submit"} variant={"raised"} color={"primary"}>Submit</Button>
</form>
</Paper>
</Grow>
);
}
}
validation component
class MuiValidator extends Component {
constructor(props) {
super(props);
this.state = {
error : "",
errorMsg: ""
};
this.isValid = this.isValid.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleChange (e) {
this.props.onChange(e.target.value);
let isValidField = this.isValid(e.target);
// console.log(e.target.appendChild());
console.log(isValidField);
};
//Validation function
isValid (input) {
console.log(input);
//check required field
if (input.getAttribute("required") !== null && input.value === "") {
this.setState({error: true,errorMsg:'This field is required'});
return false
} else {
this.setState({error: false,errorMsg:''});
// input.nextSibling.textContent = "";
}
if (input.getAttribute("type") === "email" && input.value !== "") {
if (!this.validateEmail(input.value)) {
this.setState({error: true, errorMsg: "Please enter valid email address"})
return false
} else {
this.setState({error: false,errorMsg:''});
}
}
return true;
};
validateEmail = (value) => {
let re = /^(([^<>()[\]\\.,;:\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,}))$/;
return re.test(value);
};
componentDidMount() {
if (this.props.onComponentMounted) {
this.props.onComponentMounted(this);
}
}
render() {
const {error,errorMsg} = this.state;
return (
<div>
<FormControl fullWidth={this.props.fullWidth} margin={"normal"} error={!!error}>
<InputLabel>First Name</InputLabel>
<Input name={this.props.name} type={this.props.type} placeholder={this.props.placeholder}
value={this.props.value} onChange={this.handleChange}
inputProps={this.props.inputProps} ref={this.props.name}/>
{error && <FormHelperText>{errorMsg}</FormHelperText>}
</FormControl>
</div>
);
}
}
I have no idea how can fix this please help me...
thanks all. finally, I found a solution. I changed the handle submit function like this
handleSubmit = (e) => {
e.preventDefault();
let validForm = true;
this.state.fields.forEach((field) => {
//create input element
let elm = document.createElement("input");
for (let attr in field.refs[field.props.name].props) {
if (attr !== "onChange" && attr !== "inputProps") {
elm.setAttribute(attr, field.refs[field.props.name].props[attr]);
elm.removeAttribute("onChange");
}
if (attr === "inputProps") {
for (let props in field.refs[field.props.name].props.inputProps) {
elm.setAttribute(props, field.refs[field.props.name].props.inputProps[props]);
}
}
}
if (typeof field.isValid === "function") {
let validField = field.isValid(elm);
validForm = validForm && validField
}
});
if (validForm) {
console.log(this.state.fields);
}
};
The problem resides in the on submit because the input is not retrieved correctly (I need a working code but it seems refs are not set correctly) while on event you pass the exact reference to the field coming directly from event. Check your refs and everything should work ok. As said from #SandipNirmal Formik has a great validation feature that you are substantially replicating.

Textfields component number validation React js

I have 3 Textfields, what I want to do is just to accept number so if someone put text and click continue the application should display an error saying that just numbers are allowed.
The following code displays an error message if the Textfield is empty and thats ok but the other validation to check if the user inputs text or numbers is pending and I'm stucked.
import React from 'react';
import RaisedButton from 'material-ui/RaisedButton';
import TextField from 'material-ui/TextField';
import Divider from 'material-ui/Divider';
import cr from '../styles/general.css';
export default class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
buy_: '',
and_: '',
save_: '',
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event, index, value) {
this.setState({value});
}
clear() {
console.info('Click on clear');
this.setState({
buy_: '',
and_: '',
save_: ''
});
}
validate() {
let isError = false;
const errors = {
descriptionError: ''
};
if (this.state.buy_.length < 1 || this.state.buy_ === null) {
isError = true;
errors.buy_error = 'Field requiered';
}
if (this.state.and_.length < 1 || this.state.and_ === null) {
isError = true;
errors.and_error = 'Field requiered';
}
if (this.state.save_.length < 1 || this.state.save_ === null) {
isError = true;
errors.save_error = 'Field requiered';
}
this.setState({
...this.state,
...errors
});
return isError;
}
onSubmit(e){
e.preventDefault();
// this.props.onSubmit(this.state);
console.log('click onSubmit')
const err = this.validate();
if (!err) {
// clear form
this.setState({
buy_error: '',
and_error: '',
save_error: ''
});
this.props.onChange({
buy_: '',
and_: '',
save_: ''
});
}
}
render() {
return (
<div className={cr.container}>
<div className ={cr.boton}>
<Divider/>
<br/>
</div>
<div className={cr.rows}>
<div>
<TextField
onChange={(e) => {this.setState({buy_: e.target.value})}}
value={this.state.buy_}
errorText={this.state.buy_error}
floatingLabelText="Buy"
/>
</div>
<div>
<TextField
onChange={(e) => {this.setState({and_: e.target.value})}}
value={this.state.and_}
errorText={this.state.and_error}
floatingLabelText="And"
/>
</div>
<div>
<TextField
onChange={(e) => {this.setState({save_: e.target.value})}}
value={this.state.save_}
errorText={this.state.save_error}
floatingLabelText="Save"
/>
</div>
</div>
<div className={cr.botonSet}>
<div className={cr.botonMargin}>
<RaisedButton
label="Continue"
onClick={e => this.onSubmit(e)}/>
</div>
<div>
<RaisedButton
label="Clear"
secondary ={true}
onClick={this.clear = this.clear.bind(this)}
/>
</div>
</div>
</div>
);
}
}
Can someone help me on this please.
Thanks in advance.
You can prevent users from input text by using this :
<TextField
onChange={(e) => {
if(e.target.value === '' || /^\d+$/.test(e.target.value)) {
this.setState({and_: e.target.value})
} else {
return false;
}
}}
value={this.state.and_}
errorText={this.state.and_error}
floatingLabelText="And"
/>
The TextField component can restrict users to entering text using the JavaScript test method:
<TextField
onChange={(e) => {
if(e.target.value == '' || (/\D/.test(e.target.value))) {
this.setState({and_: e.target.value})}
}
else {
return false;
}
}
value={this.state.and_}
errorText={this.state.and_error}
floatingLabelText="And"
/>
Try adding this code in your validate function.
You can use regex to validate your fields for text or numbers like :
import * as RegExp from './RegExpression';
validate() {
let isError = false;
const errors = {
descriptionError: ''
};
if (this.state.buy_ && !RegExp.NAME.test(this.state.buy_)) {
// validation check if input is name
isError = true;
errors.buy_error = 'Invalid name';
}
if (this.state.and_ && !RegExp.NUMBER.test(this.state.and_)) {
// validation check if input is number
isError = true;
errors.and_error = 'Invalid Number';
}
this.setState({
...this.state,
...errors
});
return isError;
}
In RegexExpression file add these validations like this :
export const NAME = /^[a-z ,.'-()"-]+$/i;
export const NUMBER = /^[0-9]*$/ ;
You are not initialising error object in state but accessed in TextField as this.state.and_error. Either you should initialise error in constructor like this.state = { and_error: "" } or initialise error object as
this.state = {
error: {
and_error: "",
buy_error: "",
save_error: ""
}
}
So in your TextField
<TextField
onChange={(e) => {
if(e.target.value === "" || (/\D/.test(e.target.value))) {
this.setState({and_: e.target.value})}
}
else {
return false;
}
}
value={this.state.and_}
errorText={this.state.error.and_error} // If initialised error string access as this.state.and_error
floatingLabelText="And"
/>
Your validate function will be like
validate() {
let isError = false;
const errors = this.state.errors;
if (this.state.buy_.toString().length < 1 || this.state.buy_ === null) {
isError = true;
errors.buy_error = 'Field requiered';
}
if (this.state.and_.toString().length < 1 || this.state.and_ === null) {
isError = true;
errors.and_error = 'Field requiered';
}
if (this.state.save_.toString().length < 1 || this.state.save_ === null) {
isError = true;
errors.save_error = 'Field requiered';
}
this.setState({errors});
return isError;
}
Hope this will heps you!
You can use react-validation for all validation and set rules for validation

Input not focusing on componentDidUpdate

I have an input that is disable by default, but when I dispatch an action to enable it, it should become able. I also want this input to become focused, but I am not able to do that. Here is my component:
class UserInput extends Component {
constructor (props) {
super(props);
this.state = { responseValue: '' };
this.responseHandler = this.responseHandler.bind(this);
this.submitAnswer = this.submitAnswer.bind(this);
}
componentDidUpdate (prevProps) {
if (!this.props.disable && prevProps.disable) {
this.userInput.focus();
}
}
responseHandler (e) {
this.setState({ responseValue: e.target.value });
}
submitAnswer () {
this.props.submitUserAnswer(this.state.responseValue);
this.setState({ responseValue: '' })
}
render () {
return (
<div className="input-container">
<input ref={(userInput) => { this.userInput = userInput; }}
className="input-main"
disabled={this.props.disable}
value={this.state.responseValue}
onChange={this.responseHandler}
/>
<button className="input-button" onClick={this.submitAnswer}>{this.props.strings.SEND}</button>
</div>
);
}
}
UserInput.defaultProps = {
strings: {
'SEND': 'SEND',
},
};
UserInput.contextTypes = {
addSteps: React.PropTypes.func,
};
export default Translate('UserInput')(UserInput);
Thanks in advance!
I reckon your problem lies here:
if (!this.props.disable && prevProps.disable) {
this.userInput.focus();
}
this.props.disable will still be its initial value (false) after the update (it's not being updated by a parent component from what I can see) so the call to focus is never invoked.
I ended up doing this, because I needed to also add a placeholder to the disabled input:
class UserInput extends Component {
constructor (props) {
super(props);
this.state = { responseValue: '' };
this.responseHandler = this.responseHandler.bind(this);
this.submitAnswer = this.submitAnswer.bind(this);
}
responseHandler (e) {
this.setState({ responseValue: e.target.value });
}
submitAnswer () {
this.props.submitUserAnswer(this.state.responseValue);
this.setState({ responseValue: '' })
}
render () {
return (
<div className="input-container">
{ this.props.disable
? <div className="disable-input-box">Wait to type your response</div>
: <input
className="input-main"
disabled={this.props.disable}
value={this.state.responseValue}
onChange={this.responseHandler}
autoFocus
/>
}
<button className="input-button" onClick={this.submitAnswer}>{this.props.strings.SEND}</button>
</div>
);
}
}

Categories