Pass data from child component to parent component in React - javascript

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.

Related

Test a button of a child component in the parent component

I'm new in tests and search since 3 days how to resolve my problem. Hope you could help me..
I have a parent component :
import React from 'react';
import './Subscribe.scss';
import Button from '../../Components/Button/Button';
class Subscribe extends React.Component {
state = {
user: {
firstName: '',
pseudo:'',
email: '',
password:''
}
}
handleChange = (e) => {
this.setState({
user: {...this.state.user, [e.target.name]: e.target.value}
}, () => console.log(this.state))
}
onSubmit = (e) => {
// e.preventDefault()
console.log("you've clicked")
//todo
}
render() {
return(
<form className='subscribe' id='subscriptionForm'>
<label htmlFor='firstName'>Prénom :</label>
<input
data-testid='inputString'
type='text'
name='firstName'
onChange={this.handleChange}
value={this.state.user.firstName}
/>
<label htmlFor='pseudo'>Pseudo :</label>
<input
data-testid='inputString'
type='text'
name='pseudo'
onChange={this.handleChange}
value={this.state.user.pseudo}
/>
<label htmlFor='email'>Email :</label>
<input
data-testid='inputString'
type='email'
name='email'
onChange={this.handleChange}
value={this.state.user.email}
/>
<label htmlFor='password'>
password :
</label>
<Button id='submitSubscription' text='Go go !' onSubmit={this.onSubmit}/>
<Button text='Annuler'/>
</form>
)
}
}
export default Subscribe;
A child component :
import React from "react";
const Button = (props) => {
return (
<button type="button" onClick={props.onClick}>{props.text}</button>
)
}
Button.displayName = 'Button'
export default Button
i wanna test it but nothing works...
My test :
import React from 'react';
import { shallow, mount } from 'enzyme';
import Subscribe from './Subscribe.js';
import Button from "./../../Components/Button/button.js"
describe('<LogIn />', () => {
it('Should call onSubmit on subscribe component when button component is clicked and allow user to subscribe ', () => {
// Rend le composant et les enfants et renvoie un wrapper Enzyme
const wrapper = mount(<Subscribe />);
// Trouve la première balise bouton
const button = wrapper.find("#submitSubscription");
// Récupère l'instance React du composant
const instance = wrapper.instance();
// Ecoute les appels à la fonction on Submit
jest.spyOn(instance, "onSubmit");
button.simulate('click');
expect(instance.onSubmit).toHaveBeenCalled();
})
Comments are what I tried.
The answer is still Expected number of calls: >= 1 Received number of calls: 0
I'm open to try by react test too, I begin so any help would be a pleasure.
Thanks in advance !
Your form setup has some mistakes. Mainly, you'll want to put a handleSubmit on the form's onSubmit prop and change one of the buttons to have a type prop of submit. Please see the codesandbox for a working version:
Working example (you can run the tests by clicking on the Tests tab next to the Browser tab):
This example uses some es6 syntax, so if you're unfamilar, please read the following:
Object Destructuring
Spread syntax
Arrow Function Expressions (Fat Arrow/Lamba Functions) - Implict Returns
components/Button/Button.js
import React from "react";
const Button = props => (
<button
style={{ marginRight: 10 }}
type={props.type || "button"}
onClick={props.onClick}
>
{props.text}
</button>
);
Button.displayName = "Button";
export default Button;
components/Subscribe/Subcribe.js
import React, { Component } from "react";
import Button from "../Button/Button";
// import './Subscribe.scss';
const initialState = {
user: {
firstName: "",
pseudo: "",
email: "",
password: ""
}
};
class Subscribe extends Component {
state = initialState;
handleChange = ({ target: { name, value } }) => {
// when spreading out previous state, use a callback function
// as the first parameter to setState
// this ensures state is in sync since setState is an asynchronous function
this.setState(
prevState => ({
...prevState,
user: { ...prevState.user, [name]: value }
}),
() => console.log(this.state)
);
};
handleCancel = () => {
this.setState(initialState);
};
handleSubmit = e => {
e.preventDefault();
this.props.onSubmit(this.state);
};
render = () => (
<form
onSubmit={this.handleSubmit}
className="subscribe"
id="subscriptionForm"
>
<label htmlFor="firstName">Prénom :</label>
<input
data-testid="inputString"
type="text"
name="firstName"
onChange={this.handleChange}
value={this.state.user.firstName}
/>
<br />
<label htmlFor="pseudo">Pseudo :</label>
<input
data-testid="inputString"
type="text"
name="pseudo"
onChange={this.handleChange}
value={this.state.user.pseudo}
/>
<br />
<label htmlFor="email">Email :</label>
<input
data-testid="inputString"
type="email"
name="email"
onChange={this.handleChange}
value={this.state.user.email}
/>
<br />
<label htmlFor="password">password :</label>
<input
data-testid="inputString"
type="password"
name="password"
onChange={this.handleChange}
value={this.state.user.password}
/>
<br />
<br />
<Button type="button" text="Annuler" onClick={this.handleCancel} />
<Button id="submitSubscription" type="submit" text="Soumettre" />
</form>
);
}
export default Subscribe;
components/Subscribe/__tests__/Subscribe.test.js (I'm passing in a onSubmit prop to mock it and I expect that to be called. This is a more common test case versus testing against React's event callback implementation which forces you to unnecessarily spy on the class field. By testing against the prop (or a state change or some secondary action), we already cover whether or not the callback works!)
import React from "react";
import { mount } from "enzyme";
import Subscribe from "../Subscribe.js";
const onSubmit = jest.fn();
const initProps = {
onSubmit
};
describe("<LogIn />", () => {
it("Should call onSubmit on subscribe component when button component is clicked and allow user to subscribe ", () => {
const wrapper = mount(<Subscribe {...initProps} />);
const spy = jest.spyOn(wrapper.instance(), "handleSubmit"); // not necessary
wrapper.instance().forceUpdate(); // not necessary
wrapper.find("button[type='submit']").simulate("submit");
// alternatively, you could simply use:
// wrapper.find("form").simulate("submit");
expect(spy).toHaveBeenCalledTimes(1); // not necessary
expect(onSubmit).toHaveBeenCalledTimes(1);
});
});
index.js
import React from "react";
import { render } from "react-dom";
import Subscribe from "./components/Subscribe/Subscribe";
import "./styles.css";
const onSubmit = formProps => alert(JSON.stringify(formProps, null, 4));
render(
<Subscribe onSubmit={onSubmit} />,
document.getElementById("root")
);

How can i pass props through methods inside components?

i have a react component named "List" that renders smaller components "Post" using a button through method "Addpost()" that takes 2 props from the input form. I have saved the input in 2 varables but i don't know how to pass these props to the Addpost() method inside the return of List's render().
//=========== List component ==============
class List extends React.Component{
renderPost(title,content){
return(
<Post titolo={title} contenuto={content}/>
);
}
renderPost just render the Post component in a in the HTML
addPost(title,content){
title = document.getElementById("inputTitle").value;
content = document.getElementById("inputContent").value;
console.log(title, content)
this.renderPost(title,content);
}
addPost should take the input value and use renderPost to render the Post component with that title and content
render(){
return(
<div>
{this.renderPost("testTitle","testContent")}
<form>
Title:<br></br>
<input type="text" id="inputTitle"/><br></br>
Content:<br></br>
<input type="text" id="inputContent"/>
</form><br></br>
<button className="square"
how can i make this work? title and content are not defined
onClick={() =>
this.addPost(title,content)
Add Post!
</button>
</div>
);
}
}
//=========== Post component ==============
class Post extends React.Component {
render() {
return (
<li className="w3-padding-16">
<img src="/w3images/workshop.jpg" alt="Imagedf" className="w3-left w3-margin-right" />`enter code here`
<span className="w3-large">
{this.props.titolo}
</span><br></br>
<span>{this.props.contenuto}</span>
</li>
);
}
}
Basically, whenever you're dealing with forms and inputs, you would use refs.
App.js
import React from 'react';
import logo from './logo.svg';
import './App.css';
import PostList from './components/PostList'
import AddPostForm from './components/AddPostForm'
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
posts: [] //state is handled here
}
this.addPost = this.addPost.bind(this)
}
addPost(title, content) {
let newPost = { title, content }
this.setState(({ posts }) => { return { posts: [...posts, newPost] } } )
}
render() {
const { posts } = this.state
return (
<div>
<AddPostForm onNewPost={this.addPost} /> //we pass addPost to the component
<br />
<PostList posts={posts} />
</div>
);
}
}
export default App;
Post.js
import React from 'react';
function Post({titolo, contenuto}) {
return (
<li className="w3-padding-16">
<img src="/w3images/workshop.jpg" alt="Imagedf" className="w3-left w3-margin-right" />`enter code here`
<span className="w3-large">
{titolo}
</span><br></br>
<span>{contenuto}</span>
</li>
);
}
export default Post
AddPostForm.js
import React from 'react';
const addPostForm = ({onNewPost = f => f}) => { //onNewPost method is passed by props from the parent
let _titleInput, _contentInput //these are our refs, see the docs for more information
const submit = (e) => {
e.preventDefault()
onNewPost(_titleInput.value, _contentInput.value) //here we call the addPost function that was passed to the component
_titleInput.value = '' //empty the inputs
_contentInput.value = ''
_titleInput.focus() //set focus
}
return (
<form onSubmit={submit}>
Title:<br></br>
<input type="text" ref={title => _titleInput = title} /><br></br>{/* Note the ref attribute */}
Content:<br></br>
<input type="text" ref={content => _contentInput = content} />
<button className="square">Add a new post</button>
</form>
)
}
export default addPostForm
PostList.js
import React from 'react';
import Post from './Post';
const PostList = ({ posts=[] }) => {
return (
<div className="post-list">
{
posts.map((post, index) =>
<Post key={index} titolo={post.title} contenuto={post.content} />
)
}
</div>
)
}
export default PostList
And the result:
edit
renderPost just render the Post component in a in the HTML
state = { inputTitle: '', inputContent: '' }
addPost(title,content){
title = document.getElementById("inputTitle").value;
content = document.getElementById("inputContent").value;
console.log(title, content)
this.renderPost(title,content);
}
addPost should take the input value and use renderPost to render the Post
component with that title and content
render(){
return(
<div>
{this.renderPost("testTitle","testContent")}
<form>
Title:<br></br>
<input type="text" value={this.inputTitle} onChnage={event => setState({ inputTitle: event.target.value }) }><br></br>
Content:<br></br>
<input type="text" value={this.inputContent} onChnage={event => setState({ inputContent: event.target.value }) } />
</form><br></br>
<button className="square"
on click function
onClick={() =>
this.addPost(this.inputTitle,this.inputContent)
Add Post!
</button>
</div>
);
}
}

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)}
/>

Input value is null React

I'm not sure what the deal is probably something stupid but when my handle email and handle password handlers are hit, I get null values for text that I entered on the form.
LoginContainer
import React, { Component } from 'react'
import * as AsyncActions from '../actions/User/UserAsyncActions'
import Login from '../components/Login/Login'
class LoginContainer extends Component {
constructor(props) {
super(props)
this.state = {
email: '',
password: ''
}
this.emailIsValid = this.emailIsValid.bind(this)
this.handleEmailInput = this.handleEmailInput.bind(this)
this.handlePasswordInput = this.handlePasswordInput.bind(this)
this.handleLoginPressed = this.handleLoginPressed.bind(this)
}
handlePasswordInput(e, password) {
e.persist()
this.setState(password )
}
handleEmailInput(e, email) {
e.persist()
this.setState(email)
}
handleLoginPressed(e) {
e.persist()
// e.preventDefault()
if (this.emailIsValid(this.state.email) &&
this.passwordIsValid(this.state.password)) {
this.props.login(this.state.email, this.state.password)
}
}
emailIsValid(e, email) {
e.persist()
if (!email) {
return false
}
return true
}
passwordIsValid(e, password) {
e.persist()
if (!password) {
return false
}
return true
}
render(){
return( <Login
handleEmailInput={this.handleEmailInput}
handlePasswordInput={this.handlePasswordInput}
login={this.handleLoginPressed}
/> )
}
}
const mapStateToProps = state => {
return {
requesting: state.user.requesting,
loggedIn: state.user.loggedIn,
token: state.user.token,
session: state.user.session
}
}
export const mapDispatchToProps = {
login: AsyncActions.login
}
export { Login }
export default connect(mapStateToProps, mapDispatchToProps)(LoginContainer)
Login
class Login extends Component {
render(){
return (
<div>
<LoginForm
handleEmailInput={this.props.handleEmailInput}
handlePasswordInput={this.props.handlePasswordInput}
login={this.props.login}
/>
</div>
)
}
}
export default Login
LoginForm
import React, { Component } from 'react'
import { Button, FormControl, FormGroup, ControlLabel, PageHeader } from 'react-bootstrap'
class LoginForm extends Component {
render(){
return (
<div className='ft-login-form'>
<PageHeader className='ft-header'>Login</PageHeader>
<form onSubmit={this.props.login}>
<FormGroup controlId="formBasicText" >
<ControlLabel>Email</ControlLabel>
<FormControl
bsSize="small"
className="ft-username"
componentClass="input"
onChange={this.props.handleEmailInput}
placeholder="Enter mail"
style={{ width: 300}}
type="text"
// value={this.state.email}
/>
<ControlLabel>Password</ControlLabel>
<FormControl
bsSize="small"
className="ft-password"
componentClass="input"
onChange={this.props.handlePasswordInput}
placeholder="Enter Password"
style={{ width: 300}}
type="text"
// value={this.state.password}
/>
</FormGroup>
<Button
className='ft-login-button'
type='submit'>Login</Button>
</form>
</div>)
}
}
export default LoginForm
It looks like you were on the right path with the value={this.state.password}. But since your state is in the parent component, you have to pass the state down and the value becomes value={this.props.value}. The event handlers usually look something like this:
handlePasswordInput(e, password) {
e.persist()
this.setState({ password: e.target.value })
}
They could be different due to the FormControl component but it's worth changing them to see if that's your problem. Also, onChange handlers implicitly pass in e and you have to use a arrow function expression to explicitly pass in anything else.
Edit: If they were able to do something like you mentioned in the comment, they probably did something like this:
handlePasswordInput(e, password) {
e.persist()
const password = e.target.value
this.setState({ password })
}
In es6, { password } is the same as { password: password}

Using Form and Input elements correctly

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>
);
}
}

Categories

Resources