Using Form and Input elements correctly - javascript

I'm trying to make reusable Form and Input components and so far got to following:
Form
import React from 'react'
import classNames from 'classnames/bind'
import styles from './style.scss'
const cx = classNames.bind(styles)
export default class Form extends React.Component {
constructor (props) {
super(props)
this.submitForm = this.submitForm.bind(this)
}
submitForm (e) {
e.preventDefault()
this.props.onSubmit()
}
render () {
return (
<form className={cx('Form')} onSubmit={this.submitForm}>
{this.props.children}
</form>
)
}
}
Form.propTypes = {
children: React.PropTypes.any.isRequired,
onSubmit: React.PropTypes.func.isRequired
}
Input
import React from 'react'
import classNames from 'classnames/bind'
import styles from './style.scss'
const cx = classNames.bind(styles)
export default class Input extends React.Component {
constructor (props) {
super(props)
this.state = {
value: this.props.value || ''
}
this.changeValue = this.changeValue.bind(this)
}
changeValue (e) {
this.setState({value: e.target.value})
}
render () {
return (
<div className={cx('Input')}>
<input
type={this.props.type}
value={this.state.value}
required={this.props.required}
onChange={this.changeValue}
placeholder={this.props.placeholder || ''}
noValidate />
</div>
)
}
}
Input.propTypes = {
type: React.PropTypes.string.isRequired,
value: React.PropTypes.string,
required: React.PropTypes.bool,
placeholder: React.PropTypes.string
}
I then use them within some another component, lets say HomePageComponent
<Form onSubmit={this.loginUser}>
<Input type='email' placeholder='email' />
<Input type='password' placeholder='password' />
<Input type='submit' value='Submit' />
</Form>
This all works well, but how would I get input values and use them to set state of HomePageComponent to this.state= { email: [value from input email], password: [value from password input] }

You don't need to store the value in the Input component.
You can get the input values by keeping references to individual inputs:
<Form onSubmit={this.loginUser}>
<Input ref='email' type='email' placeholder='email' />
<Input ref='password' type='password' placeholder='password' />
<Input type='submit' value='Submit' />
</Form>
Then, in loginUser you can access these using:
const email = ReactDOM.findDOMNode(this.refs.email).value;
const password = ReactDOM.findDOMNode(this.refs.password).value;

if you add the name attribute to your input elements, you can access their values this way. Hope this helps
import React, { PropTypes, Component } from 'react';
class Form extends Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(e) {
e.preventDefault();
const form = e.target.elements;
console.log(form.email.value);
console.log(form.password.value);
}
render() {
return (
<form className={cx('Form')} onSubmit={this.handleSubmit}>
<Input type='email' placeholder='email' name='email' />
<Input type='password' placeholder='password' name='password' />
<button type="submit">Submit</button>
</form>
);
}
}

Related

Can't edit value of input element in React

I have a problem with my React project, I have an input element like below but I can't edit the text of the input. I just can edit the input text only when I delete the value attribute, but I want a default value for it.
<div className="form-group">
<label>Email:</label>
<input
className="form-input"
type="text"
value="l.h.thuong181#gmail.com"
name="email">
</input>
</div>
If you've an uncontrolled component, you might want to use the defaultValue property instead of value (See this reference)
You can use useRef or defaultValue
import React, { useRef, useEffect } from "react";
const input = useRef();
useEffect(() => {
input.current.value = "l.h.thuong181#gmail.com";
}, []);
<input ref={input} className="form-input" type="text" name="email" />`
Here is a sample code how to use inputs in react
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" />
</form>
);
}
}
Set the default value inside the state object and attach a change handler to the input to capture the changes:
Sample codesandbox: https://codesandbox.io/s/react-basic-class-component-22fl7
class Demo extends React.Component {
constructor(props) {
super(props);
this.state = {
inputValue: 'l.h.thuong181#gmail.com'
};
}
handleChange = event => {
this.setState({ inputValue: event.target.value }, () =>
console.log(this.state.inputValue)
);
};
render() {
return (
<div className="form-group">
<label>Email:</label>
<input
className="form-input"
type="text"
value={this.state.inputValue}
onChange={this.handleChange}
name="email">
</input>
</div>
);
}
}

Get search value in state in ReactJS

I am new in ReactJS and trying to get the search input box value and store it in a state.
My code:
<form className="search-form" onSubmit={this.handleSubmit}>
<input type="search" name="search" ref={(input) => this.query = input}
placeholder="Search..." />
<button className="search-button" type="submit" id="submit">Go!</button>
</form>
Inside constructor:
this.handleSubmit = this.handleSubmit.bind(this);
And
handleSubmit = e => {
e.preventDefault();
this.setState({search: this.query.value});
console.log(this.state.search) //Returns undefined
e.currentTarget.reset();
}
Any help is highly appreciated.
I would suggest you to use controlled input approach.
state = {
value: null,
}
handleValue = (e) => this.setState({ value: e.target.value });
Then you don't have to use ref.
<input
type="search"
name="search"
onChange={this.handleValue}
placeholder="Search..."
/>
This code may help you:
import React from 'react';
class search extends React.Component{
constructor(props)
{
super(props);
this.state = {value: ''};
this.addValue = this.addValue.bind(this);
this.updateInput = this.updateInput.bind(this);
}
addValue(evt)
{
evt.preventDefault();
if(this.state.value !==undefined)
{
alert('Your input value is: ' + this.state.value)
}
}
updateInput(evt){
this.setState({value: evt.target.value});
}
render()
{
return (
<form onSubmit={this.addValue}>
<input type="text" onChange={this.updateInput} /><br/><br/>
<input type="submit" value="Submit"/>
</form>
)
}
}
export default search;

ReactJS: Why are the constructor called when setState changes the state

I'm a newbie to ReactJS and I have made an app where you can submit a name and email. The name and mail should be displayed in a list at the bottom of the page. It is displayed for a short period, but then the constructor gets called and clears the state and the list.
Why is the constructor called after the state change? I thought the constructor only runs once and then the render-method runs after setState() changes the state.
class App extends React.Component {
constructor(props) {
super(props);
console.log("App constructor");
this.state = {
signedUpPeople: []
};
this.signUp = this.signUp.bind(this);
}
signUp(person) {
this.setState({
signedUpPeople: this.state.signedUpPeople.concat(person)
});
}
render() {
return (
<div>
<SignUpForm signUp={this.signUp} />
<SignedUpList list={this.state.signedUpPeople} />
</div>
);
}
}
class SignUpForm extends React.Component {
constructor(props) {
super(props);
console.log("SignUpForm constructor");
this.state = {
name: "",
email: ""
};
this.changeValue = this.changeValue.bind(this);
this.onSubmitForm = this.onSubmitForm.bind(this);
}
changeValue(event) {
const value = event.target.value;
const name = event.target.name;
this.setState({
name: value
});
}
onSubmitForm() {
this.props.signUp(this.state);
this.setState({
name: "",
email: ""
});
}
render() {
console.log("SignUpForm render");
return (
<div>
<h2>Sign up</h2>
<form onSubmit={this.onSubmitForm}>
<label htmlFor="name">Name:</label>
<input id="name" name="name" onChange={this.changeValue} />
<br />
<label htmlFor="email">E-mail:</label>
<input id="email" name="name" onChange={this.changeValue} />
<input type="submit" value="Sign up" />
</form>
</div>
);
}
}
class SignedUpList extends React.Component {
render() {
console.log("SignedUpList render");
return (
<div>
<h2>Already signed up</h2>
<ul>
{this.props.list.map(({ name, email }, index) => (
<li key={index}>
{name}, {email}
</li>
))}
</ul>
</div>
);
}
}
ReactDOM.render(<App />, window.document.getElementById("app"));
See CodePen example
The default behavior of a form with input of type submit is to post back to the server.
<input> elements of type "submit" are rendered as buttons. When the
click event occurs (typically because the user clicked the button),
the user agent attempts to submit the form to the server.
You can pass the event object of the submit handler and use the event.preventDefault method to prevent the form from posting back:
onSubmitForm(e) {
e.preventDefault();
this.props.signUp(this.state);
this.setState({
name: "",
email: ""
});
}
Here is a running snippet of your code:
class App extends React.Component {
constructor(props) {
super(props);
console.log("App constructor");
this.state = {
signedUpPeople: []
};
this.signUp = this.signUp.bind(this);
}
signUp(person) {
this.setState({
signedUpPeople: this.state.signedUpPeople.concat(person)
});
}
render() {
return (
<div>
<SignUpForm signUp={this.signUp} />
<SignedUpList list={this.state.signedUpPeople} />
</div>
);
}
}
class SignUpForm extends React.Component {
constructor(props) {
super(props);
console.log("SignUpForm constructor");
this.state = {
name: "",
email: ""
};
this.changeValue = this.changeValue.bind(this);
this.onSubmitForm = this.onSubmitForm.bind(this);
}
changeValue(event) {
const value = event.target.value;
const name = event.target.name;
this.setState({
name: value
});
}
onSubmitForm(e) {
e.preventDefault();
this.props.signUp(this.state);
this.setState({
name: "",
email: ""
});
}
render() {
//console.log('SignUpForm render');
return (
<div>
<h2>Sign up</h2>
<form onSubmit={this.onSubmitForm}>
<label htmlFor="name">Name:</label>
<input id="name" name="name" onChange={this.changeValue} />
<br />
<label htmlFor="email">E-mail:</label>
<input id="email" name="name" onChange={this.changeValue} />
<input type="submit" value="Sign up" />
</form>
</div>
);
}
}
class SignedUpList extends React.Component {
render() {
//console.log('SignedUpList render');
return (
<div>
<h2>Already signed up</h2>
<ul>
{this.props.list.map(({ name, email }, index) => (
<li key={index}>
{name}, {email}
</li>
))}
</ul>
</div>
);
}
}
ReactDOM.render(<App />, window.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">
</div>
onSubmitForm(e) {
e.preventDefault(); // prevent the form from refreshing the page
this.props.signUp(this.state);
this.setState({
name: "",
email: ""
});
}
It is working with your codepen link :)
Have a deep look at this :
React - Preventing Form Submission
or
https://medium.com/#ericclemmons/react-event-preventdefault-78c28c950e46
Thank you all for your help! :-)
e.preventDefault();
Did fix the problem. Thanks!

Submitting a Redux Form

I've got a Redux form which I've attempted to break into several subcomponents as a wizard, like this: https://redux-form.com/7.0.4/examples/wizard/
However, I'm having trouble properly wiring the form into my actions in order to submit the form data. The example actually passes the onSubmit method into the form from the router, which I don't want to do; rather, I'd like to connect my form to my actions, and then pass the signUpUser method into the last of the three components making up the wizard. My current attempt to do so is throwing two errors:
Uncaught TypeError: handleSubmit is not a function
Warning: Failed prop type: The prop `onSubmit` is marked as required in `SignUp`, but its value is `undefined`.
My original form component worked fine, but the same logic does not work in the new component. I think it's a question of scoping, but not sure. I'm relatively new to React and Redux-Form, so am finding this hard to reason through. Ideas?
New (broken) component:
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import * as actions from '../../actions';
import SignupFirstPage from './signupComponents/signupFirstPage';
import SignupSecondPage from './signupComponents/signupSecondPage';
import SignupThirdPage from './signupComponents/SignupThirdPage';
class SignUp extends Component {
constructor(props) {
super(props);
this.nextPage = this.nextPage.bind(this);
this.previousPage = this.previousPage.bind(this);
this.state = {
page: 1
};
this.handleFormSubmit = this.handleFormSubmit.bind(this);
}
nextPage() {
this.setState({ page: this.state.page + 1 });
}
previousPage() {
this.setState({ page: this.state.page - 1 });
}
handleFormSubmit(props) {
this.props.signUpUser(props);
}
render() {
const { handleSubmit } = this.props;
const { page } = this.state;
return (
<div>
{page === 1 && <SignupFirstPage onSubmit={this.nextPage} />}
{page === 2 && (
<SignupSecondPage
previousPage={this.previousPage}
onSubmit={this.nextPage}
/>
)}
{page === 3 && (
<SignupThirdPage
previousPage={this.previousPage}
onSubmit={handleSubmit(this.handleFormSubmit.bind(this))}
/>
)}
<div>
{this.props.errorMessage &&
this.props.errorMessage.signup && (
<div className="error-container">
Oops! {this.props.errorMessage.signup}
</div>
)}
</div>
</div>
);
}
}
function mapStateToProps(state) {
return {
errorMessage: state.auth.error
};
}
SignUp.propTypes = {
onSubmit: PropTypes.func.isRequired
};
export default connect(mapStateToProps, actions)(SignUp);
New subcomponent (the final one):
import React from 'react';
import { Field, reduxForm } from 'redux-form';
import validate from './validate';
import renderField from '../../renderField';
const SignupThirdPage = props => {
const { handleSubmit, pristine, previousPage, submitting } = props;
return (
<form onSubmit={handleSubmit}>
<Field
name="password"
type="password"
component={renderField}
label="Password"
/>
<Field
name="passwordConfirm"
type="text"
component={renderField}
label="Confirm Password"
/>
<div>
<button
type="button"
className="previous btn btn-primary"
onClick={previousPage}>
Previous
</button>
<button
className="btn btn-primary"
type="submit"
disabled={pristine || submitting}>
Submit
</button>
</div>
</form>
);
};
export default reduxForm({
form: 'wizard', //Form name is same
destroyOnUnmount: false,
forceUnregisterOnUnmount: true,
validate
})(SignupThirdPage);
Old component:
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { Field, reduxForm } from 'redux-form'
import * as actions from '../../actions'
import { Link } from 'react-router';
const renderField = ({ input, label, type, meta: { touched, error, warning } }) => (
<fieldset className="form-group">
<label htmlFor={input.name}>{label}</label>
<input className="form-control" {...input} type={type} />
{touched && error && <span className="text-danger">{error}</span>}
</fieldset>
)
class SignUp extends Component {
constructor(props)
{
super(props);
this.handleFormSubmit = this.handleFormSubmit.bind(this);
}
handleFormSubmit(props) {
// Sign user up
this.props.signUpUser(props);
}
render() {
const { handleSubmit } = this.props;
return (
<div className="form-container">
<form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))}>
<Field name="firstName" component={renderField} type="text" label="First Name" />
<Field name="lastName" component={renderField} type="text" label="Last name" />
<Field name="email" component={renderField} type="email" label="Email" />
<Field name="company" component={renderField} type="text" label="Company"/>
<Field name="password" component={renderField} type="password" label="Password" />
<Field name="password_confirmation" component={renderField} type="password" label="Password Confirmation" />
<div>
{this.props.errorMessage && this.props.errorMessage.signup &&
<div className="error-container">Oops! {this.props.errorMessage.signup}</div>}
</div>
<button type="submit" className="btn btn-primary">Sign Up</button>
</form>
</div>
);
}
}
function validate(values) {
let errors = {}
if (values.password !== values.password_confirmation) {
errors.password = 'Password and password confirmation don\'t match!'
}
return errors
}
function mapStateToProps(state) {
return {
errorMessage: state.auth.error
}
}
SignUp = reduxForm({ form: 'signup', validate })(SignUp);
export default connect(mapStateToProps, actions)(SignUp);
The answer:
Redux-Form says:
You can access your form's input values via the aptly-named values
prop provided by the redux-form Instance API.
This is how it works: since my component is connected, I have access to my actions in its props. Therefore, in my render method, I can simply pass the signUpUser method into the subcomponent, like this:
<SignupThirdPage
previousPage={this.previousPage}
signingUp={this.signingUp}
onSubmit={values => this.props.signUpUser(values)}
/>
Your problem is this:
{page === 3 && (
<SignupThirdPage
previousPage={this.previousPage}
onSubmit={handleSubmit(this.handleFormSubmit.bind(this))}
/>
)}
If you carefully look at your code you will find that the function handleSubmit does not exist.
I think you were meaning to write this:
<SignupThirdPage
previousPage={this.previousPage}
onSubmit={this.handleFormSubmit.bind(this)}
/>

Pass data from child component to parent component in React

I am currently building a simple React application. I have some data passed down to a window.example object, from a laravel application. I would then like to access the data, and i have found out i can do that initially with props. But the problem is that when i submit a form, in AssignmentForm Component, i want to update the data in the AssignmentBox, which shows the data, and add a row of data. How would i do that?
So my structure looks like this:
DashboardApp
AssignmentBox
AssignmentFormNew
This is my code:
main.js:
import swal from 'sweetalert';
import $ from 'jquery';
import React from 'react';
import { render } from 'react-dom';
import DashboardApp from './components/Dashboard';
render(
<DashboardApp tracks={window.tracks} assignments={window.assignments} />,
document.getElementById('content')
);
Dashboard.js:
import React from 'react';
import {Grid, Row, Col} from 'react-bootstrap';
import TrackBox from './TrackBox';
import TrackFormStop from './TrackFormStop';
import TrackFormNew from './TrackFormNew';
import AssignmentBox from './AssignmentBox';
import AssignmentFormNew from './AssignmentFormNew';
class DashboardApp extends React.Component {
render () {
return (
<Grid>
<Row>
<Col md={12}>
<AssignmentBox assignments={this.props.assignments} />
<AssignmentFormNew />
</Col>
</Row>
</Grid>
)
}
}
export default DashboardApp;
AssignmentBox.js
import React from 'react';
import {Panel} from 'react-bootstrap';
const title = (
<h2>Current Assignments</h2>
);
class AssignmentBox extends React.Component {
render () {
return (
<Panel header={title}>
<ul>
{this.props.assignments.map(item => (
<li key={item.id}>{item.title}</li>
))}
</ul>
</Panel>
)
}
}
export default AssignmentBox;
AssignmentFormNew.js
import React from 'react';
import {Panel, Button, FormGroup, ControlLabel} from 'react-bootstrap';
import $ from 'jquery';
const title = (
<h2>New Assignment</h2>
);
const footer = (
<Button bsStyle="primary" type="submit" block>New Assignment</Button>
);
class AssignmentFormNew extends React.Component {
constructor (props) {
super(props);
this.state = {
title: '',
description: '',
customer: '',
date: ''
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleTitleChange = this.handleTitleChange.bind(this);
this.handleDescriptionChange = this.handleDescriptionChange.bind(this);
this.handleCustomerChange = this.handleCustomerChange.bind(this);
this.handleDeadlineChange = this.handleDeadlineChange.bind(this);
}
handleSubmit (e) {
e.preventDefault();
console.log(window.Laravel.csrfToken);
$.ajax({
url: '/assignment',
type: 'POST',
data: {
_token: window.Laravel.csrfToken,
title: this.refs.title.value,
description: this.refs.description.value,
customer: this.refs.customer.value,
deadline: this.refs.deadline.value
},
success: res => {
this.setState({
title: '',
description: '',
customer: '',
deadline: ''
});
console.log(res);
},
error: (xhr, status, err) => {
console.error(status, err.toString());
}
});
}
handleTitleChange (event) {
this.setState({title: event.target.value});
}
handleDescriptionChange (event) {
this.setState({description: event.target.value});
}
handleCustomerChange (event) {
this.setState({customer: event.target.value});
}
handleDeadlineChange (event) {
this.setState({deadline: event.target.value});
}
render () {
return (
<form onSubmit={this.handleSubmit}>
<Panel header={title} footer={footer}>
<FormGroup controlId="assignmentTitle">
<ControlLabel>Title</ControlLabel>
<input type="text" ref="title" placeholder="e.g. Crowdfunding module for prestashop" className="form-control" value={this.state.title} onChange={this.handleTitleChange} />
</FormGroup>
<FormGroup controlId="assignmentDescription">
<ControlLabel>Description</ControlLabel>
<textarea className="form-control" ref="description" rows="10" value={this.state.description} onChange={this.handleDescriptionChange} />
</FormGroup>
<FormGroup controlId="assignmentCustomer">
<ControlLabel>Customer</ControlLabel>
<input type="text" placeholder="e.g. John Doe" ref="customer" className="form-control" value={this.state.customer} onChange={this.handleCustomerChange} />
</FormGroup>
<FormGroup controlId="assignmentDeadline">
<ControlLabel>Deadline</ControlLabel>
<input type="date" className="form-control" ref="deadline" value={this.state.deadline} onChange={this.handleDeadlineChange} />
</FormGroup>
</Panel>
</form>
)
}
}
export default AssignmentFormNew;
Thank you in advance.
Put your handleSubmit() function in Dashboard.js, and add the following code
constructor (props) {
super(props);
this.state = {
assignments:this.props.assignments
};
}
handleSubmit (e) {
... your ajax code
this.setState({assignments:res})
}
<AssignmentBox assignments={this.state.assignments} handleSubmit={this.handleSubmit}/>
Then in AssignmentFormNew.js change:
<form onSubmit={this.props.handleSubmit}>
Basically when you click submit, it call parent's handleSubmit function in Dashboard.js, then after your ajax call, update the state so the AssignmentBox will re-render it with new data.
Create a method in assignment box to update the data and pass that function as a prop to the assignment form. Call the function within assignment form and pass the data.
in AssignmentFormNew.js
handleSubmit (e) {
e.preventDefault();
this.props.childValuesToParent(e);
.....
}
now this props is available inside parent - Dashboard.js like this
<AssignmentFormNew childValuesToParent={this.handleChange} />
somewhat like call back.

Categories

Resources