React FormControl onChange not being triggered - reactjs

I am trying to get a simple form working in React but I can't seem to get it working. I am also using react-bootstrap for my GUI components.
I have a component called Inventory that holds the form:
class Inventory extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.state = {
'upc' : ''
}
}
handleChange(event) { this.setState({'upc' : event.target.value }); }
handleSubmit(event) {
// Do some stuff
}
render() {
return (
<Container fluid>
<h1>Inventory Page</h1>
<Row>
<Col>
<Form onSubmit={this.handleSubmit}>
<Form.Group className="mb3" controlId="invUpcForm">
<Form.Label>UPC</Form.Label>
<Form.Control type="text" placeholder="123456789012" />
<Form.Text value={this.state.upc} onChange={this.handleChange} className="text-muted">
Ensure the field is focused, and scan the barcode.
</Form.Text>
</Form.Group>
<Button variant="primary" type="submit">Submit</Button>
</Form>
</Col>
</Row>
</Container>
);
}
}
However, whenever I enter any text into the Input field, handleChange never gets called despite the value of the field changing. If I submit the field, checking the value of upc in state shows it is empty ''.
What am I doing wrong?

According to this example from the react-bootstrap docs, you should be attaching your value and onChange props to the <Form.Control /> component, not <Form.Text />. Form text is for help text, while the form controls handle the actual input elements.

You gotta change like this.
render() {
return (
<Container fluid>
<h1>Inventory Page</h1>
<Row>
<Col>
<Form onSubmit={this.handleSubmit}>
<Form.Group className="mb3" controlId="invUpcForm">
<Form.Label>UPC</Form.Label>
<Form.Control
type="text"
placeholder="123456789012"
value={this.state.upc}
onChange={this.handleChange}
/>
<Form.Text className="text-muted">
Ensure the field is focused, and scan the barcode.
</Form.Text>
</Form.Group>
<Button variant="primary" type="submit">Submit</Button>
</Form>
</Col>
</Row>
</Container>
);
}
It is because Form.Control is a real input component, and Form.Text component is just a simple div/span to render text. You can check it by inspecting element in chrome browser.

Related

Inputs not updating state in functional react component

I looked around and I see similar questions, but whenever I follow the answers I can't seem to get this to work in the way that I have it written. I am starting off all four states as blank inside of an array, but I want to update the states as the user types for each input field. Why is setChanging not working to update the state for the particular name of the input field? Console logs both the x and y values as I type into each input. I must be doing something simple wrong, but I can't figure out what it is.
const ContactForm = () => {
const initialValues = {
recipientName: "",
recipientEmail: "",
fromName: "",
fromEmail: "",
};
const [changing, setChanging] = useState(initialValues);
const handleInputChange = (e) => {
let x = e.target.name;
let y = e.target.value;
console.log(x);
console.log(y);
setChanging({...changing, [e.target.name]: e.target.value});
console.log(initialValues);
}
return (
<Form>
<Form.Group className="mb-3">
<Row>
<Col>
<Form.Control
type="text"
required
name="recipientName"
placeholder="Recipient Name*"
id="form.recipientName"
onChange={handleInputChange}
/>
</Col>
<Col>
<Form.Control
type="email"
required
name="recipientEmail"
placeholder="Recipient Email*"
id="form.recipientEmail"
onChange={handleInputChange}
/>
</Col>
</Row>
</Form.Group>
<Form.Group className="mb-3">
<Row>
<Col>
<Form.Control
type="text"
required
name="fromName"
placeholder="From Name*"
aria-invalid="form.fromName"
onChange={handleInputChange}
/>
</Col>
<Col>
<Form.Control
type="email"
required
name="fromEmail"
placeholder="From Email*"
id="form.fromEmail"
onChange={handleInputChange}
/>
</Col>
</Row>
</Form.Group>
<Button variant="primary" type="submit">
Submit
</Button>
</Form>
);
}
export default ContactForm
You're logging the initialValues:
console.log(initialValues);
So you're always seeing the value of initialValues, which never changes. Nowhere are you observing state.
You can respond to state updates and log the updated state with useEffect for example:
useEffect(() => console.log(changing), [changing]);
This would log the value of changing any time that value changes.
You'd also observe updates to state in the UI if/when your UI uses state to render output. (Currently it does not.)
There are some things I suggest you to change:
<Form.Control
type="text"
required
name="fromName"
placeholder="From Name*"
aria-invalid="form.fromName"
onChange={handleInputChange}
/>
I'm not sure if those components belong to a framework like MaterialUI but would be better to have an attribute called value where you pass the state to handle a controlled component instead of an uncontrolled component:
<Form.Control
type="text"
required
name="fromName"
placeholder="From Name*"
aria-invalid="form.fromName"
onChange={handleInputChange}
value={changing.fromName} // Add this attribute
/>
Also, would be better if your initialState is outside of the function.
console.log(initialValues);
You should print the state instead of the initialValues, what you are updating is the state, not the initialValues.
setChanging({...changing, [e.target.name]: e.target.value});
this is because you didn't specify value attribute on your inputs.
in addition to this to see the changes on user type you must console.log the state (which is change here) not the initialValues.
example:
<Form.Control
type="text"
required
name="recipientName"
placeholder="Recipient Name*"
id="form.recipientName"
value={changing.fromName}
onChange={handleInputChange}
/>

react-bootstrap - Form.Control defaultValue not updated when re-rendered

When I call setState and my form is re-rendered, the value of the text input doesn't change.
import React, { Component } from 'react';
import { Button, Col, Form, Row } from 'react-bootstrap';
export default class Test extends Component {
constructor(props) {
super(props);
this.state = {
status: "new"
}
}
approve = (e) => {
this.setState({
status: "I'm newer than you!"
});
e.preventDefault();
}
render() {
return (
<div>
Status is {this.state.status}
<Form onSubmit={this.approve}>
<Form.Group as={Row} controlId="status">
<Form.Label column >Status</Form.Label>
<Col>
<Form.Control readOnly type="text" size="sm" defaultValue={this.state.status} />
</Col>
</Form.Group>
<Form.Group as={Row}>
<Button type="submit">approve</Button>
</Form.Group>
</Form>
</div>
);
}
}
When I click on the button, I see the static text ("Status is ...") get updated but the input field does not. Have I wrongly assumed the text input should update?
If you want to change it, you can replace 'defaultValue' with 'value' in FormControl component.
<Form.Control readOnly type="text" size="sm" value={this.state.status} />

My form does not accept the input value from a user

I have a login form i created with react-strap I am facing a problem with input value, the form behave as readonly for the first input of email but accept the second input of the password.
I have tried to delete the email field to trace if the password field will fail to accept input but it worked perfect
I have attached my component code, Please i will appreciate anyone who can help to solve this problem
import React, { Component } from 'react';
import { Button, Card, CardBody, Col, Container, Form, Input, InputGroup, Row } from
'reactstrap';
import './Login.css'
class Login extends Component {
constructor(props) {
super(props)
this.state = {
email:'',
password:''
}
}
changeHandler = (e) =>{
this.setState({[e.target.name]:e.target.value });
}
onSubmit = (e) =>{
e.preventDefault();
fetch('http://localhost:5000/user/login',{
method:'POST',
body:JSON.stringify(this.state),
})
.then(response =>{
if(response.status === 200){
this.props.history.push('/')
}else{
const error = new Error(response.error);
throw error;
}
})
.catch(err=>{
console.error(err);
alert('Ooops! Login failed please check your email and password, then try again')
})
}
render() {
return (
<div className="app flex-row align-items-center">
<Container className="container">
<Row className="justify-content-center">
<Col md="12" lg="10" xl="8">
<Card className="mx-4">
<CardBody className="p-4">
<Form onSubmit={this.onSubmit} className="login-form">
<h1>Login Form</h1>
<InputGroup className="mb-3">
<Input type="email "
name="email "
required="required"
placeholder="email "
value={this.state.email }
onChange={this.changeHandler}
/>
</InputGroup>
<InputGroup className="mb-3">
<Input type="password"
name="password"
required="required"
placeholder="Password"
value={this.state.password}
onChange={this.changeHandler}
/>
</InputGroup>
<Row>
<Col xs="12" sm="6">
<Button type="submit" className="btn btn-info mb-1" block><span>Login</span>
</Button>
Home
</Col>
</Row>
</Form>
</CardBody>
</Card>
</Col>
</Row>
</Container>
</div>
)
}
}
export default Login;
The issue is you have a space in your input name:
<Input type="email "
name="email "
^ remove this space
As a result of that, your change handler fails to set the state because it's setting "email " not "email".
You also have a space in your input type and placeholder.
The problem may be in changeHandler. As you pass it to a input like
onChange={this.changeHandler}
The this in
changeHandler = (e) =>{
this.setState({[e.target.name]:e.target.value });
}
refers to input and not to Class component, so it does not update the state.
To fix this the problem you should bind the Class component this to changeHandler.
constructor(props) {
super(props);
this.state = {
email:'',
password:''
}
this.changeHandler = this.changeHandler.bind(this);
}
You may also read this article.

Insert component with variables into another component

I'm attempting to pass a component into another component in react.
The problem I'm getting with different attempts is that some variables in my component are appearing as undefined.
I have a class component like so:
class PleaseUpVote extends Component {
constructor(props) {
super(props)
this.state = { ...INITIAL_STATE };
}
render() {
const { first, last, phone, email, address, address2, city,
zip, headOfHousehold, household, firstHousehold, lastHousehold } = this.state;
return (
<>
<UserHeader />
{/* Page content */}
<Container className="mt--7" fluid>
<Row>
<Col className="order-xl-2 mb-5 mb-xl-0" xl="4">
<Card className="card-profile shadow">
<Row className="justify-content-center">
<Col className="order-lg-2" lg="3">
<div className="card-profile-image">
<a href="#pablo" onClick={e => e.preventDefault()}>
<img
alt="..."
className="rounded-circle"
src={require("assets/img/theme/team-4-800x800.jpg")}
/>
</a>
</div>
</Col>
</Row>
</Container>
</>
)
}
}
In it I have a form that is an input form. The input form should be input if it has not been saved. If it has been saved then it should not be input by default.
My solution was to add the input form as a separate mmethod like so:
const InputForm = () => (
<>
<FormGroup>
<label
className="form-control-label"
htmlFor="input-country"
>
First
</label>
<Input
className="form-control-alternative"
name="firstHousehold"
value={firstHousehold}
onChange={this.onChange}
id="input-firstHousehold"
placeholder="First"
type="text"
/>
</FormGroup>
</>
)
Then just insert this bad boy into PleaseUpVote component like so:
</Col>
<InputForm/>
</Row>
And bam, works like a charm! Right?!... Wrong!
I get that the variables I've placed in input are undefined. How would I be able to achieve this?
The actual idea is, if you want variables or function from parent component to be used in child component, you need to simply pass them from parent component.
<InputForm firstHousehold={firstHousehold} onChange={this.onChange}/>
Of course you need to create onChange function in parent component which handles input change in child component.
Your child component should be,
const InputForm = (props) => (
<>
<FormGroup>
<label
className="form-control-label"
htmlFor="input-country"
>
First
</label>
<Input
className="form-control-alternative"
name="firstHousehold"
value={props.firstHousehold} //access using props
onChange={props.onChange} //access using props
id="input-firstHousehold"
placeholder="First"
type="text"
/>
</FormGroup>
</>
)

How to get an input value using "refs" in react-bootstrap form?

I'm trying to create a form that appears in modal. So when user input a value, that value is stored in local storage. Here's a picture that help's you to understand what I mean:
Here is the code of the form:
function FieldGroup({id, label, help, ...props}) {
return (
<ReactBootstrap.FormGroup controlId={id}>
<ReactBootstrap.ControlLabel>{label}</ReactBootstrap.ControlLabel>
<ReactBootstrap.FormControl {...props} />
{help && <ReactBootstrap.HelpBlock>{help}</ReactBootstrap.HelpBlock>}
</ReactBootstrap.FormGroup>
);
}
const formInstance = (
<form>
<FieldGroup
id="formControlsText"
type="text"
label="Text"
placeholder="Recipe Name"
inputRef={ref => { this.input = ref; }}
/>
<ReactBootstrap.FormGroup controlId="formControlsTextarea">
<ReactBootstrap.ControlLabel>Ingredients</ReactBootstrap.ControlLabel>
<ReactBootstrap.FormControl componentClass="textarea" placeholder="Enter Ingredients" />
</ReactBootstrap.FormGroup>
</form>
);
As I've read in bootstrap React tutorial, I should add
<FormControl inputRef={ref => { this.input = ref; }} /> to the FormControl props. But after adding it I get an error when modal form is invoked:
`
I just made this issue. My code:
<FormControl
componentClass="input"
placeholder="Enter recipe title"
inputRef={(ref) => {this.input = ref}}
defaultValue={title}/>
</FormGroup>
And then you can get the value from <FormControl> in some handler like this:
console.log(this.input.value);
Details can be found in my repo: https://github.com/kerf007/recipebook
I have same problem with you, and this is my solution
const FieldGroup = ({id, label, help, inputRef, ...props}) =>
<FormGroup controlId={id}>
<ControlLabel>{label}</ControlLabel>
<FormControl {...props} inputRef={inputRef}/>
{help && <HelpBlock>{help}</HelpBlock>}
</FormGroup>
and my form
<form>
<FieldGroup
id="bookName"
type="text"
label="Name"
placeholder="Enter name..."
inputRef = {(input) => this.inputName = input }
/>
<FieldGroup
id="bookAuthor"
label="Author"
type="text"
placeholder="author 's name..."
inputRef={(input) => this.inputAuthor = input}
/>
</form>
then you can get book 's name and author 's name value by:
this.inputName.value and this.inputAuthor.value
This issue (or more like a change in the way it works) is related to React-Bootstrap. The way you're doing it won't work anymore.
The <FormControl> component directly renders the or other specified component. If you need to access the value of an uncontrolled <FormControl>, attach a ref to it as you would with an uncontrolled input, then call ReactDOM.findDOMNode(ref) to get the DOM node. You can then interact with that node as you would with any other uncontrolled input.
Here's an example:
var React = require('react');
var ReactDOM = require('react-dom');
var FormControl = require('react-bootstrap').FormControl;
React.createClass({
render: function() {
return (<FormControl ref="formControl" />);
},
getFormControlNode: function() {
// Get the underlying <input> DOM element
return ReactDOM.findDOMNode(this.refs.formControl);
}
});
As soon as you get the DOM element, you will be able to retrieve the value: this.getFormControlNode().value or do anything else that you want.
PS: Here's a related github issue on this topic.
This worked for me, using https://reactjs.org/docs/refs-and-the-dom.html
constructor(props) {
super(props);
this.email = React.createRef();
}
submit() {
var email = this.email.current.value;
console.log(email);
}
render() {
return (
<Form>
<Form.Control type="email" placeholder="Your email" ref={this.email} />
<Button variant="primary" onClick={()=>this.submit()}>Send</Button>
</Form>
);
}
I think what it suggests to use is the ref callback attribute, so just change inputRef to ref.
FYI: https://facebook.github.io/react/docs/refs-and-the-dom.html
Hello this solution worked for me!
<Form
noValidate
validated={validated}
onSubmit={(e) => this.handleSubmit(e)}
style={{ width: '100%' }}
>
<Form.Group controlId="formBasicEmail">
<Form.Label>Email address</Form.Label>
<Form.Control type="email" placeholder="Enter email" inputRef={(ref) => { this.email = ref }} required />
<Form.Text className="text-muted"> Well never share your email with anyone else.
</Form.Text>
</Form.Group>
</Form>
handleSubmit(event) {
console.log(event.target.elements.formBasicPassword.value)
}
I think I found how to get the ref from React-Bootstrap <Form/>.
import React, {createRef} from 'react'
interface definedProps {}
interface definedState {
myRef: Object //I didn't found the more accurate type
}
export default class SomeClass extends React.Component<definedProps,definedState> {
state = {
myRef: React.createRef<Form<any>>() //That's it!
}
const handleClose = () => {
console.log('this.state.myRef: ', this.state.myRef); //Here we can take data from Form
debugger; //todo: remove
}
render() {
return (
<div>
<Form ref={this.state.myRef}> { /*Here we're connecting the form's ref to State*/}
<Form.Group controlId='formName'>
<Form.Control
type='text'
placeholder='Enter Name'
/>
</Form.Group>
...
<Button
variant='primary'
onClick={handleClose}
>
Save Changes
</Button>
</Form>
</div>
)
}
}

Resources