React - Input Value Clears Out On Submit - javascript

I'm trying to implement some validation for an email based input. It's mostly there, but I'm running into a strange condition.
I'm debouncing the onChange function that includes some validation and validating again on submit. If the user submits an invalid string before the debounce catches it, the submit function will catch it and set the component state to invalid, but only until the debounce hits, at which point the debounce sets the state back to valid, because it seems like the form submit is clearing out the underlying value of the input field. I have a preventDefault in my submit function, but it still seems like the event.target is getting emptied whenever that submit function is fired. I would like to keep the event.target/input value in sync. Here is my code:
import React from 'react';
import {Link} from 'react-router';
import _ from 'underscore';
import classNames from 'classnames';
class Search extends React.Component {
constructor(props) {
super(props);
this.state = {
isValid: true
};
this.validateOnChange = _.debounce(this.validateOnChange,500).bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.validateEmail = this.validateEmail.bind(this);
}
handleSubmit(event) {
event.preventDefault();
if(this.validateEmail(event.target[0].value)){
$.ajax({
url: '/api/search?email=' + event.target[0].value
})
.done((data) => {
this.props.getNewImage(data, false);
})
.fail(() => {
this.props.getNewImage('/img/obi.gif', true);
});
}else {
this.setState({isValid: false});
}
}
validateOnChange(event){
this.setState({isValid: (event.target.value ? this.validateEmail(event.target.value) : true)});
}
validateEmail(input){
var re;
re = /[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*#(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+(?:[A-Z]{2}|com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|museum)\b/i;
return re.test(input);
}
render() {
var classes = classNames({
'form-control': true,
'valid': this.state.isValid,
'invalid': this.state.isValid === false
});
return(
<form ref='searchForm' className='navbar-form navbar-left animated' onSubmit={this.handleSubmit.bind(this)}>
<div className='input-group'>
<input type='text' className={classes} placeholder="Enter Email!" onChange={this.validateOnChange.bind(this)} />
<span className='input-group-btn'>
<button className='btn btn-default' type="submit"><span className='glyphicon glyphicon-search'></span>Search</button>
</span>
</div>
{this.state.isValid ? null : <span className="invalid-text">Invalid Email Address</span> }
</form>
);
}
}
export default Search;

you can use linkState of react/linkState to handle the onChange event of an input element.
<input
min="1"
max="10"
type="number"
className="form-control"
valueLink={linkState(this, 'qty')} />

Related

How to submit an input field with enter in React

I've made an input field in react that takes in a value and is supposed to be submitted using the enter key but I haven't been able to get it to work.
<input
key = "searchBox"
type = "text"
value = {this.state.currentValue}
placeholder = "Search"
onChange = {this.handleChange}
onSubmit = {this.handleSubmit}
/>
At the moment, enter does nothing. I've tried wrapping this in a form element where onSubmit is now part of that form element but when I do that, the entire input field disappears. So, how can I submit this by using enter? Here's the rest of the code.
import React from 'react';
export default class searchClass extends React.Component {
render() {
return (
<form onSubmit = {() => alert("yes")}>
<input
key = "searchBox"
type = "text"
value = {this.state.currentValue}
placeholder = "Search"
onChange = {this.handleChange}
/>
</form>
)
}
}
Here's part of my render function in App.js.
return (
<div className = "tableWrapper">
<div id = "tableScroll">
<table>
<SearchClass />
</table>
</div>
</div>
)
try this.
import React from 'react';
export default class searchClass extends React.Component {
render() {
return (
<form onSubmit={() => alert("yes")}>
<input
key="searchBox"
type="text"
value={this.state.currentValue}
placeholder="Search"
onChange={this.handleChange}
/>
<input type="submit"/>
</form>
)
}
}
Issue
input elements don't really do anything with an onSubmit prop/attribute.
<input
key = "searchBox"
type = "text"
value = {this.state.currentValue}
placeholder = "Search"
onChange = {this.handleChange}
onSubmit = {this.handleSubmit} // useless
/>
Solution
Wrap your input in a form and prevent the default form action from occurring, this is reloading the page, and your app.
class SearchClass extends React.Component {
state = {
currentValue: ""
};
handleChange = (e) => this.setState({ currentValue: e.target.value });
render() {
return (
<form
onSubmit={(e) => {
e.preventDefault(); // <-- prevent the default form action
alert("yes");
}}
>
<input
key="searchBox"
type="text"
value={this.state.currentValue}
placeholder="Search"
onChange={this.handleChange}
/>
</form>
);
}
}
Do you want to make a search box/input? You can use <input type="search"/>. Wrap it with form tag.

Why is submit state not updating after change of input right away?

please help me wit this code. I am struggling to update state right away after input is inserted. I was trying to do it with onSubmit method at least to sync input === submit after clicking on Button but still no luck as per console log.
See console picture:
enter image description here
How should I do it?
import React from 'react';
import './Search.css';
const results = ["Balaton", "Zamardi", "Sound", "Madarsko", "Sziget", "Hungary"]
class Search extends React.Component {
constructor(props) {
super(props);
this.state = {
input: '',
submit: ''
};
this.onInput = this.onInput.bind(this);
this.onSubmit = this.onSubmit.bind(this);
}
onInput(event) {
this.setState({
input: event.target.value,
submit: this.state.input});
console.log(this.state.input)
console.log(this.state.submit)
}
onSubmit(event) {
event.preventDefault()
if (results.includes(this.state.input)){
return alert("This is correct")
} else {
return alert("This not correct")
}
}
render() {
return(
<div>
<form className="search-form" onSubmit={this.onSubmit}>
<input type="text" value={this.state.input} placeholder="Our great memory" onChange={this.onInput}/>
<button type="submit">Try that!</button>
</form>
</div>
)
}
};
export default Search;
I re-wrote your component for readability, I believe you error is simply that setstate is async. This means that when you tried to set the state of submit at the same time as input, submit would always be one behind. By adding the callback in onInput after input has been set you should get the correct value ready to be submitted
import React, { Component } from 'react';
const results = ["Balaton", "Zamardi", "Sound", "Madarsko", "Sziget", "Hungary"]
class Search extends Component {
state = {
input: '',
submit: ''
};
// Added callback after input setstate
onInput = (event) => {
this.setState({
input: event.target.value}, () => this.setState({submit: this.state.input));
console.log(this.state.input)
console.log(this.state.submit)
}
onSubmit = (event) => {
event.preventDefault()
if (results.includes(this.state.input)){
return alert("This is correct")
} else {
return alert("This not correct")
}
}
render() {
return(
<div>
<form className="search-form" onSubmit={this.onSubmit}>
<input
type="text"
value={this.state.input}
placeholder="Our great memory"
onChange={this.onInput}/>
<button type="submit">Try that!</button>
</form>
</div>
)
}
};
export default Search;
onInput(event) {
this.setState({
input: event.target.value,
submit: event.target.value});,() =>
console.log(this.state.input)
console.log(this.state.submit)
}
This will let you see the correct values in log too.
Your submit state doesn't update as you assign the old value of this.state.input to it rather you should assign event.target.value.

How not to refresh the page after onClick

I have small class in react, i want to display the result on the screen after i click on the button, but before the display happens, the page reload.
how do i do it?
what am I missing?
import React, {Component} from 'react';
class InputFieldWithButton extends Component{
constructor(props){
super();
this.state = {
message: ''
};
}
handleChange(e){
this.setState({
message: e.target.value
});
}
doSomething(e){
return(
<h1>{this.state.message}</h1>
)
}
render(){
return (
<div>
<form >
<input type="text" placeholder="enter some text!" value=
{this.state.message}
onChange={this.handleChange.bind(this)}/>
<button onClick={this.doSomething.bind(this)}>Click me</button>
</form>
</div>
)
}
}
export default InputFieldWithButton;
Your button is inside a form and triggering a submit.
You can use the preventDefault() method to stop it from doing so:
doSomething(e) {
e.preventDefault();
return (
<h1>{this.state.message}</h1>
)
}
By the way, your return statement of this click handler makes no sense at the moment.
Edit
As a followup to your comment:
Can you explain me what is my mistake in the return?
Not really a mistake, but it is useless in this context as your are not doing anything with the returned object.
Where and how do you expect to use the <h1>{this.state.message}</h1> that you are returning?
If you intend to show / hide the input message in your screen you could do it with conditional rendering.
Just store a bool like showMessage in your state and render the message only if it's set to true.
Here is a small example:
class InputFieldWithButton extends React.Component {
constructor(props) {
super(props);
this.state = {
message: '',
showMessage: false
};
}
handleChange = (e) => {
this.setState({
message: e.target.value
});
}
toggleMessage = (e) => {
e.preventDefault();
this.setState({ showMessage: !this.state.showMessage })
}
render() {
const { showMessage, message } = this.state;
return (
<div>
<form >
<input
type="text"
placeholder="enter some text!"
value={message}
onChange={this.handleChange}
/>
<button onClick={this.toggleMessage}>Toggle Show Message</button>
{showMessage && <div>{message}</div>}
</form>
</div>
)
}
}
ReactDOM.render(<InputFieldWithButton />, document.getElementById('root'));
<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="root"></div>
By the way, it is considered as bad practice to bind the functions inside the render method, because you are creating a new instance of a function on each render call. instead do it inside the constructor which will run only once:
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
Or you can use arrow functions which will reference this in a lexical context:
handleChange = (e) => {
this.setState({
message: e.target.value
});
}
This is what i've used in my example.
you're not specifying the buttons'type
<button type="button">
Set the type attribute on the button to be button. The default is submit since it is wrapped in a form. So your new button html should look like this:
<button type="button" onClick={this.doSomething.bind(this)}>Click me</button>

How to pass forms event to a parent component to preventDefault?

I have a quite standard form:
export default class Form extends React.Component {
constructor (props) {
super(props)
this.state = {
email: '',
refCode: ''
}
this.onSubmit = this.onSubmit.bind(this)
this.changeEmail = this.changeEmail.bind(this)
}
changeEmail (event) {
this.setState({ email: event.target.value })
}
onSubmit () {
this.props.onSubmit(this.state)
}
render () {
return (
<div>
<h2>{this.props.title}</h2>
<form className={cx('Form')} onSubmit={this.onSubmit}>
<input
className={cx('Form-email')}
onChange={this.changeEmail}
value={this.state.email}
type='email'
placeholder='email' />
<input className={cx('Form-refcode')} type='text' placeholder='enter referal code' />
<input className={cx('Form-btn')} type='submit' value='sign up' />
</form>
</div>
)
}
}
I then want to handle forms submission in a parent component, via this function
submitForm (value) {
event.preventDefault()
console.log(value.email)
}
/* ... */
<Form
title='What are you waiting for?! Sign up now'
onSubmit={this.submitForm} />
The issue I encountered is that event.preventDefault() is not working, nor does it seem that I am getting correct value logged via console.
I assume I am not passing or receiving values here correctly but I have no idea where i'm going wrong.
In child component pass event(you can name it as you want) argument to method from parent component
onSubmit (event) {
this.props.onSubmit(event, this.state)
}
In parent component do like this
submitForm (event, value) {
event.preventDefault()
console.log(value.email)
}

react-bootstrap: clear element value

I'm trying to clear my input fields after an onClick event.
I'm using react-bootstrap library and while there is a getValue() method, there is not setValue(value) method.
I've stumbled upon this discussion .
I did not fully understand what they are suggesting in order to simply clean a form after submission.
After all, If I would use a simple HTML <input> instead of react-bootstrap I could grab the node via element ref and set it's value to be empty string or something.
What is considered a react way to clean my react-bootstrap <Input /> element?
Store the state in your React component, set the element value via props, get the element value via event callbacks. Here is an example:
Here is an example taken directly from their documentation. I just added a clearInput() method to show you you can clear the input by just updating the state of your component. This will trigger a re-render which will cause the input value to update.
const ExampleInput = React.createClass({
getInitialState() {
return {
value: ''
};
},
validationState() {
let length = this.state.value.length;
if (length > 10) return 'success';
else if (length > 5) return 'warning';
else if (length > 0) return 'error';
},
handleChange() {
// This could also be done using ReactLink:
// http://facebook.github.io/react/docs/two-way-binding-helpers.html
this.setState({
value: this.refs.input.getValue()
});
},
// Example of how you can clear the input by just updating your state
clearInput() {
this.setState({ value: "" });
},
render() {
return (
<Input
type="text"
value={this.state.value}
placeholder="Enter text"
label="Working example with validation"
help="Validation is based on string length."
bsStyle={this.validationState()}
hasFeedback
ref="input"
groupClassName="group-class"
labelClassName="label-class"
onChange={this.handleChange} />
);
}
});
For what I'm doing at the moment, I didn't really think it was necessary to control the Input component's value through setState/Flux (aka I didn't want to deal with all the boilerplate)...so here's a gist of what I did. Hopefully the React gods forgive me.
import React from 'react';
import { Button, Input } from 'react-bootstrap';
export class BootstrapForm extends React.Component {
constructor() {
super();
this.clearForm = this.clearForm.bind(this);
this.handleSave = this.handleSave.bind(this);
}
clearForm() {
const fields = ['firstName', 'lastName', 'email'];
fields.map(field => {
this.refs[field].refs['input'].value = '';
});
}
handleSubmit() {
// Handle submitting the form
}
render() {
return (
<div>
<form>
<div>
<Input
ref='firstName'
type='text'
label='First Name'
placeholder='Enter First Name'
/>
<Input
ref='lastName'
type='text'
label='Last Name'
placeholder='Enter Last Name'
/>
<Input
ref='email'
type='email'
label='E-Mail'
placeholder='Enter Email Address'
/>
<div>
<Button bsStyle={'success'} onClick={this.handleSubmit}>Submit</Button>
<Button onClick={this.clearForm}>Clear Form</Button>
</div>
</div>
</form>
</div>
);
}
}
If you use FormControl as an input, and you want to use ref to change/get value from it, you use inputRef instead of ref
<FormControl inputRef={input => this.inputText = input} .../>
and use this to get/change its value:
this.inputText.value
This worked for me in case someone else is looking for an answer :D
You can access the values of react-bootstrap by using
console.log(e.target.form.elements.fooBar.value)
You can clear them by using
e.target.form.elements.fooBar.value = ""
import React from 'react';
import {Button, Form} from 'react-bootstrap';
export default function Example(props) {
const handleSubmit = (e) => {
// Handle submitting the form
}
const resetSearch = (e) => {
e.preventDefault();
e.target.form.elements.fooBar.value = ""
}
render() {
return (
<Form onSubmit={handleSubmit} onReset={resetSearch} >
<Form.Control type="input" name="fooBar" />
<Button type="submit"> Submit </Button>
<Button onClick={resetSearch} type="submit" > Reset </Button>
</Form>
);
}
}
You can also just use ReactDOM:
<FormControl
componentClass="select"
ref="sStrike">
<option value="-">Select…</option>
<option value="1">1</option>
<option value="2">2</option>
</FormControl>
Now a different FormControl fires an onChange={handleChange} and in that handler you can just id the ref and set to the default value:
ReactDOM.findDOMNode(this.refs.sStrike).value = '-';
and that will set the select box to the 'default' value.
Just add a button in-side form with the attribute type="reset"
<Button variant="primary" type="reset">Reset</Button>

Categories