I have two components :
LoginForm which is used to render the form to login in the app
LoginPage which get the data entered in the LoginForm component and send it to a server
For the moment I would like to handle the form submit and the change of an input value. I read these two articles in the react official website to help me :
https://reactjs.org/docs/lifting-state-up.html
https://reactjs.org/docs/forms.html
But I still don't detect the submit and the change from the LoginPage component when I'm entering a value in LoginForm.
Can you help me to see where is my mistake ?
Thanks by advance.
My two components :
LoginPage.js
class LoginPage extends Component {
constructor(props) {
super(props);
this.state = {
login: true, //switch between Login and SignUp
email: '',
password: '',
firstName: '',
lastName: ''
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
}
handleSubmit(){
alert("SUBMIT");
}
handleInputChange(event) {
alert("YOUHOU");
const target = event.target;
const value = target.value;
const name = target.name;
this.setState({
[name]: value
});
alert("YEEEP");
}
render(){
return (
<div>
<div>
{this.state.login ?
<Login onSubmit={this.handleSubmit} onChange={this.handleInputChange}/>
:
<Register />
}
</div>
<a
onClick={() => this.setState({ login: !this.state.login })}
>
{this.state.login ? 'Besoin d\'un compte ?' : 'Déjà un compte ?'}
</a>
</div>
)
}
}
LoginForm.js
class LoginForm extends Component {
render(){
return (
<div>
<Card>
<form onSubmit={this.props.handleSubmit}>
<div>
<div>
<TextField name="email" floatingLabelText="Email" errorText="Champ obligatoire" type="text" onChange={this.props.handleInputChange}/>
</div>
<div>
<TextField name="password" floatingLabelText="Mot de passe" errorText="Champ obligatoire" type="password" onChange={this.props.handleInputChange} />
</div>
<CardActions>
<div>
<RaisedButton label="Se connecter" primary={true} type="submit" fullWidth />
</div>
</CardActions>
</div>
</form>
</Card>
</div>
);
}
}
handleInputChange is passed down to LoginForm as onChange prop and similarly handleSubmit is passed down by the name onSubmit and hence you need to use it like
class LoginForm extends Component {
render(){
return (
<div>
<Card>
<form onSubmit={this.props.onSubmit}>
<div>
<div>
<TextField name="email" floatingLabelText="Email" errorText="Champ obligatoire" type="text" onChange={this.props.onChange}/>
</div>
<div>
<TextField name="password" floatingLabelText="Mot de passe" errorText="Champ obligatoire" type="password" onChange={this.props.onChange} />
</div>
<CardActions>
<div>
<RaisedButton label="Se connecter" primary={true} type="submit" fullWidth />
</div>
</CardActions>
</div>
</form>
</Card>
</div>
);
}
}
Related
I have a Home component that fetches data from the database and show it.
import React, {Component} from 'react';
import { Jumbotron,Modal,Button,ModalHeader,ModalBody,
Form,FormGroup,Input,Label} from 'reactstrap';
import { Card, CardTitle, CardText, Row, Col } from 'reactstrap';
import { CardHeader, CardFooter, CardBody } from 'reactstrap';
import axios from 'axios';
import BlogAddForm from './BlogAddForm';
import NavbarComponent from './NavbarComponent';
class HomeComponent extends Component{
constructor(){
super();
this.state={
isSignupModalOpen:false,
isLoginModalOpen:false,
isblogOpen:false,
blogs:[]
}
this.toggleSignupModal=this.toggleSignupModal.bind(this);
this.toggleLoginModal=this.toggleLoginModal.bind(this);
this.toggleblogModal=this.toggleblogModal.bind(this);
}
componentDidMount() {
console.log("component did mount");
axios.get('http://localhost:3000/')
.then(response => {
if (response.data.length > 0) {
this.setState({
blogs: response.data
});
}
})
.catch((error) => {
console.log(error);
})
}
toggleSignupModal(){
this.setState({
isSignupModalOpen:!this.state.isSignupModalOpen
})
}
toggleLoginModal(){
this.setState({
isLoginModalOpen:!this.state.isLoginModalOpen
})
}
toggleblogModal(){
this.setState({
isblogOpen:!this.state.isblogOpen
})
}
render(){
console.log("inside render ");
var b=this.state.blogs.map((blog)=>{
console.log(blog);
var taggu=blog.tags.map((tag)=>{
return(
<span>#{tag}</span>
)
});
var con=blog.content.slice(0,100);
return(
<Col className="my-1" lg="4" sm="6" >
<Card key={blog._id}>
<CardHeader tag="h3">{blog.topic}</CardHeader>
<CardBody>
<CardTitle tag="h5">By {blog.writer.username}</CardTitle>
<CardText>{con}... </CardText>
<Button>Learn More</Button>
</CardBody>
<CardFooter>{taggu}</CardFooter>
</Card>
</Col>
)
});
return (
<div className="App">
<NavbarComponent />
<Jumbotron>
<h1 className="display-5">WELCOME TO BLOGERA</h1>
<p className="lead">This is a simple blog site where you can put your thoughts into words and
interact with people.</p>
<p className="my-2">
<Button color="success" onClick={this.toggleblogModal}>Add Blog</Button>
</p>
</Jumbotron>
{b}
<Modal isOpen={this.state.isLoginModalOpen} toggle={this.toggleLoginModal}>
<ModalHeader toggle={this.toggleLoginModal}>Login</ModalHeader>
<ModalBody>
<Form onSubmit={this.handleLogin}>
<FormGroup>
<Label htmlFor="username">Username</Label>
<Input type="text" id="username" name="username" innerRef=
{(input)=>this.username=input}/>
</FormGroup>
<FormGroup>
<Label htmlFor="password">Password</Label>
<Input type="password" id="password" name="password" innerRef=
{(input)=>this.password=input} />
</FormGroup>
<FormGroup check>
<Label check>
<Input type="checkbox" name="remember" innerRef=
{(input)=>this.remember=input}/>
Remember me
</Label>
</FormGroup>
<Button type="submit" value="submit" color="primary">Login</Button>
</Form>
</ModalBody>
</Modal>
<Modal isOpen={this.state.isSignupModalOpen} toggle={this.toggleSignupModal}>
<ModalHeader toggle={this.toggleSignupModal}>Signup</ModalHeader>
<ModalBody>
<Form onSubmit={this.handleLogin}>
<FormGroup>
<Label htmlFor="username">Username</Label>
<Input type="text" id="username" name="username" innerRef=
{(input)=>this.username=input}/>
</FormGroup>
<FormGroup>
<Label htmlFor="password">Password</Label>
<Input type="password" id="password" name="password" innerRef=
{(input)=>this.password=input} />
</FormGroup>
<FormGroup check>
<Label check>
<Input type="checkbox" name="remember" innerRef=
{(input)=>this.remember=input}/>
Remember me
</Label>
</FormGroup>
<Button type="submit" value="submit" color="primary">Signup</Button>
</Form>
</ModalBody>
</Modal>
<Modal isOpen={this.state.isblogOpen} toggle={this.toggleblogModal}>
<ModalHeader toggle={this.toggleblogModal}>Write Blog</ModalHeader>
<ModalBody>
<BlogAddForm toggleblogModal={this.toggleblogModal} />
</ModalBody>
</Modal>
<Row>
<Col className="my-1" lg="4" sm="6" >
<Card>
<CardHeader tag="h3">Header</CardHeader>
<CardBody>
<CardTitle tag="h5">Special Title Treatment</CardTitle>
<CardText>With supporting text below as a natural lead-in to additional content.</CardText>
<Button>Learn More</Button>
</CardBody>
<CardFooter>Footer</CardFooter>
</Card>
</Col>
<Col className="my-1" lg="4" sm="6" >
<Card>
<CardHeader tag="h3">Featured</CardHeader>
<CardBody>
<CardTitle tag="h5">Special Title Treatment</CardTitle>
<CardText>With supporting text below as a natural lead-in to additional content.</CardText>
<Button>Learn More</Button>
</CardBody>
<CardFooter className="text-muted">Footer</CardFooter>
</Card>
</Col>
<Col className="my-1" lg="4" sm="6" >
<Card>
<CardHeader tag="h3">Featured</CardHeader>
<CardBody>
<CardTitle tag="h5">Special Title Treatment</CardTitle>
<CardText>With supporting text below as a natural lead-in to additional content.</CardText>
<Button>Learn More</Button>
</CardBody>
<CardFooter className="text-muted">Footer</CardFooter>
</Card>
</Col>
</Row>
</div>
);
}
}
export default HomeComponent;
BLOGADDFORM IS a component which takes data and send post request at backend. I want that as database gets updated my homecomponent should re render with the updated data. I tried redirecting to my home component but that does not solve the purpose.
this is my blog add form component
import React, {Component} from 'react';
import { Button,Form,FormGroup,Input,Label} from 'reactstrap';
import { Redirect } from 'react-router-dom';
import axios from 'axios';
class BlogAddForm extends Component{
constructor(){
super();
this.state = {
input: {},
errors: {}
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
let input = this.state.input;
input[event.target.name] = event.target.value;
this.setState({
input:input
});
}
handleSubmit(event) {
event.preventDefault();
if(this.validate()){
console.log(this.state);
axios.post("http://localhost:3000/nb", {
params:{
data:this.state.input
}
}).then((res)=>{
let input = {};
input["topic"] = "";
input["content"] = "";
input["tag"] = "";
this.setState({input:input});
alert('Blog Added');
this.props.toggleblogModal();
<Redirect to='/' />
}).catch((err)=>{
console.log(err);
})
}
}
validate(){
let input = this.state.input;
let errors = {};
let isValid = true;
if (!input["topic"]) {
isValid = false;
errors["topic"] = "Please enter the topic of blog.";
}
if (!input["content"]) {
isValid = false;
errors["content"] = "Please write some content.";
}
if (!input["tag"]) {
isValid = false;
errors["tag"] = "Please enter tags related to this blog.";
}
this.setState({
errors: errors
});
return isValid;
}
render(){
return(
<Form onSubmit={this.handleSubmit}>
<FormGroup>
<Label htmlFor="topic">Topic</Label>
<Input type="text" id="topic" name="topic"
value={this.state.input.topic}
onChange={this.handleChange}
/>
<div className="text-danger">{this.state.errors.topic}</div>
</FormGroup>
<FormGroup>
<Label htmlFor="content">Content</Label>
<Input type="textarea" id="content" name="content" rows={8}
value={this.state.input.content}
onChange={this.handleChange}
/>
<div className="text-danger">{this.state.errors.content}</div>
</FormGroup>
<FormGroup>
<Label htmlFor="tag">Tags</Label>
<Input type="text" id="tag" name="tag"
value={this.state.input.tag}
onChange={this.handleChange}
/>
<div className="text-danger">{this.state.errors.tag}</div>
</FormGroup>
<Button type="submit" value="submit" color="primary">Submit</Button>
</Form>
)
}
}
export default BlogAddForm;
You'll need to use componentDidUpdate() when reentering the Home component view.
Extract your network request into a different function and call it from both componentDidMount() and componentDidUpdate().
I am new to react and have this form:
class CustomForm extends React.Component {
handleFormSubmit = (e) => {
e.preventDefault();
const title = e.target.elements.title.value;
const content = e.target.elements.content.value;
console.log(title, content)
}
render() {
return (
<div>
<Form onSubmit={ this.handleFormSubmit }>
<FormItem label='Title'>
<Input name='title' placeholder='Put a title here' />
</FormItem>
<FormItem label='Content'>
<Input name='content' placeholder='Enter some content' />
</FormItem>
<FormItem>
<Button type='primary' htmlType='submit'>Submit</Button>
</FormItem>
</Form>
</div>
)
}
}
The form is not submitting anything/nothing in console log. I tried it with onSubmitCapture and that seems to work. How do I fix this?
From your code it looks like you are using some custom component <Form>.. this is not the normal <form> because custom <Form> component might not have the prop onSubmit. Go through the documentation of the component you are using.
button with type='submit' will trigger the form onSubmit Handler
You need to bind the event handlers in the constructor in order to work. Read more here https://reactjs.org/docs/handling-events.html:
class CustomForm extends React.Component {
constructor(props) {
super(props)
this.handleFormSubmit = this.handleFormSubmit.bind(this)
}
handleFormSubmit = (e) => {
e.preventDefault();
const title = e.target.elements.title.value;
const content = e.target.elements.content.value;
console.log(title, content)
}
render() {
return (
<div>
<Form onSubmit={ this.handleFormSubmit }>
<FormItem label='Title'>
<Input name='title' placeholder='Put a title here' />
</FormItem>
<FormItem label='Content'>
<Input name='content' placeholder='Enter some content' />
</FormItem>
<FormItem>
<Button type='primary' htmlType='submit'>Submit</Button>
</FormItem>
</Form>
</div>
)
}
}
I would like to make my spinner appear upon the user clicking login. I'm checking to see if loading is true in login() method. If it is, make the spinner appear. I've been struggling with this for hours, can't see what am I doing wrong.
What am I doing wrong and how can I fix it?
import React, {Component} from 'react';
import fire from '../../config/Fire';
import classes from './Login.css';
import Spinner from '../../UI/Spinner/Spinner';
import { Link } from 'react-router-dom';
class Login extends Component {
constructor(props) {
super(props);
this.state = {
email: '',
password: '',
loading: false
};
this.login = this.login.bind(this);
this.handleChange = this.handleChange.bind(this);
this.signup = this.signup.bind(this);
}
handleChange(e) {
this.setState({[e.target.name]: e.target.value});
}
login() {
fire.auth().signInWithEmailAndPassword(this.state.email, this.state.password).then((u) => {
}).catch((error) => {
console.log(error);
});
this.setState({loading: true});
if(this.state.loading) {
return(
<Spinner/>
);
}
}
signup(e) {
e.preventDefault();
fire.auth().createUserWithEmailAndPassword(this.state.email, this.state.password).then((u) => {
}).then((u) => {
console.log(u)
})
.catch((error) => {
console.log(error);
})
}
render() {
return (
<div className={classes.Middle}>
<div className="form-group">
<h1>Email address</h1>
<input value={this.state.email} onChange={this.handleChange} type="email" name="email"
className="form-control" placeholder="Enter email"/>
</div>
<div className="form-group">
<h1>Password</h1>
<input value={this.state.password} onChange={this.handleChange} type="password" name="password"
className="form-control" placeholder="Password"/>
</div>
<Link to={{
pathname: '/ChooseTruck'
}}>
<button type="submit" onClick={this.login.bind(this)} className="btn btn-primary">Login</button>
</Link>
<button onClick={this.signup}>Signup</button>
</div>
);
}
}
export default Login;
I see what your problem is, you are trying to return the loader in the login function which is doable, but not with your current implementation. What I would suggest you do is to put the <Spinner /> component into the return of the render and only show it when the state is loading.
Something like this:
render() {
return (
this.state.loading ? <Spinner /> : <div> rest of your code </div>
)
}
Here you are saying, if the state of loading is true, then render the spinner, otherwise show the rest of the page. This is a much better approach for what you are trying to accomplish.
You can also remove the peace of code from the login function that returns the Spinner component.
if(this.state.loading) {
return(
<Spinner/>
);
}
Hit me up if you have any questions.
Hope this helps. :)
login should be a non-render method which makes your login request, it happens on an event. Your loading spinner should go in the render method (or a function called by it).
login() {
this.setState({loading: true});
fire.auth().signInWithEmailAndPassword(this.state.email, this.state.password)
.then((u) => {
console.log(u);
})
.catch(console.error)
.then(() => this.setState({loading: false}))
}
render() {
return (
<div className={classes.Middle}>
<div className="form-group">
<h1>Email address</h1>
<input value={this.state.email} onChange={this.handleChange} type="email" name="email"
className="form-control" placeholder="Enter email"/>
</div>
<div className="form-group">
<h1>Password</h1>
<input value={this.state.password} onChange={this.handleChange} type="password" name="password"
className="form-control" placeholder="Password"/>
</div>
<Link to={{
pathname: '/ChooseTruck'
}}>
<button type="submit" onClick={this.login.bind(this)} className="btn btn-primary">Login</button>
</Link>
<button onClick={this.signup}>Signup</button>
{this.state.loading && <Spinner/>}
</div>
);
}
I'm developing a dynamic component where the input can be used for several types: text, password, number, date, etc. The idea is to use this input, no matter the type and where to implement it, as long its adaptable. I thought using state was a nice idea, but I have no clue how to do this. Any thoughts?
import React, { Component } from 'react';
import './styles.css';
export default class InputField extends Component {
constructor(props) {
super(props);
this.state = {
name: '',
password: false,
type: ''
}
}
render () {
return (
<div>
<label className='f-size'>{this.state.name}</label>
<input
className='input'
name={this.state.name}
placeholder={this.state.name}
value={this.props.value}
type={this.state.type}
onChange={this.props.onChange}
/>
<span className="errorMessage">{this.props.error}</span>
<span className="errorMessage">{this.props.missField}</span>
</div>
)
}
}
Thank you!
I personally think you should control this via props, seeing as the value will only be meaningful to the Input's parent.
I used this
const InputField = ({
name,
placeholder,
value,
type,
onChange,
error,
missField
}) => (
<div>
<label className="f-size">{name}</label>
<input
className="input"
name={name}
placeholder={placeholder}
value={value}
type={type}
onChange={onChange}
/>
<span className="errorMessage">{error}</span>
<span className="errorMessage">{missField}</span>
</div>
);
Parent component:
class App extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
state = {
value: '',
password: '',
};
handleChange(event) {
this.setState({ [event.target.name]: event.target.value });
}
render() {
return (
<div className="App">
<InputField
value={this.state.value}
type="number"
name="value"
onChange={this.handleChange}
/>
<InputField
value={this.state.password}
type="password"
name="password"
onChange={this.handleChange}
/>
</div>
);
}
}
Code Sandbox: https://codesandbox.io/s/y4ljv75k9
Edited to used a stateless component. Not sure if you want state to handle error messages but from your example, this is a valid solution.
<InputField type="text" />
<InputField type="password" />
<input
className='input'
name={this.state.name}
placeholder={this.state.name}
value={this.props.value}
type={this.props.type}
onChange={this.props.onChange}
/>
I would use props to change the type and manage the component.
You could then control the component from a form definition
You should use props not state, so you can pass
<InputType type="text" />
<InputType type="password" />
<InputType type="number" />
and for the other params you can use props also.
You could use this.props.type but the standard jsx input component is already dynamic as you can see from my example below :
var root = document.getElementById('root');
class InputField extends React.Component {
render() {
return (
<div>
<input type={this.props.type} />
</div>
)
}
}
class App extends React.Component {
render() {
return (
<div>
<input type='date' />
<InputField type='password'/>
</div>
)
}
}
ReactDOM.render(<App />, 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>
Is there a reason why you would like to use a custom input component?
How to set defaultValue to input component?
<Field name={`${prize}.rank`} defaultValue={index} component={Input} type='text'/>
I tried like above but my fields are empty. I'm trying to create fieldArray (dynamic forms):
{fields.map((prize, index) =>
<div key={index} className="fieldArray-container relative border-bottom" style={{paddingTop: 35}}>
<small className="fieldArray-title marginBottom20">Prize {index + 1}
<button
type="button"
title="Remove prize"
className="btn btn-link absolute-link right"
onClick={() => fields.remove(index)}>Delete</button>
</small>
<div className="row">
<div className="col-xs-12 col-sm-6">
<Field name={`${prize}.rank`} defaultValue={index} component={Input} type='text'/>
<Field name={`${prize}.prizeId`} defaultValue={index} component={Input} type='text'/>
<Field
name={`${prize}.name`}
type="text"
component={Input}
label='Prize Name'/>
</div>
<div className="col-xs-12 col-sm-6">
<Field
name={`${prize}.url`}
type="text"
component={Input}
label="Prize URL"/>
</div>
<div className="col-xs-12">
<Field
name={`${prize}.description`}
type="text"
component={Input}
label="Prize Description" />
</div>
</div>
</div>
)}
On redux forms you can call initialize() with an object of values like so:
class MyForm extends Component {
componentWillMount () {
this.props.initialize({ name: 'your name' });
}
//if your data can be updated
componentWillReceiveProps (nextProps) {
if (/* nextProps changed in a way to reset default values */) {
this.props.destroy();
this.props.initialize({…});
}
}
render () {
return (
<form>
<Field name="name" component="…" />
</form>
);
}
}
export default reduxForm({})(MyForm);
This way you can update the default values over and over again, but if you just need to do it at the first time you can:
export default reduxForm({values: {…}})(MyForm);
This jsfiddle has an example
https://jsfiddle.net/bmv437/75rh036o/
const renderMembers = ({ fields }) => (
<div>
<h2>
Members
</h2>
<button onClick={() => fields.push({})}>
add
</button>
<br />
{fields.map((field, idx) => (
<div className="member" key={idx}>
First Name
<Field name={`${field}.firstName`} component="input" type="text" />
<br />
Last Name
<Field name={`${field}.lastName`} component="input" type="text" />
<br />
<button onClick={() => fields.remove(idx)}>
remove
</button>
<br />
</div>
))}
</div>
);
const Form = () => (
<FieldArray name="members" component={renderMembers} />
);
const MyForm = reduxForm({
form: "foo",
initialValues: {
members: [{
firstName: "myFirstName"
}]
}
})(Form);
this is my implementation using a HoC
import { Component } from 'react'
import {
change,
} from 'redux-form'
class ReduxFormInputContainer extends Component{
componentDidMount(){
const {
initialValue,
meta,
} = this.props
if(initialValue === undefined || meta.initial !== undefined || meta.dirty) return
const {
meta: { form, dispatch },
input: { name },
} = this.props
dispatch(change(form, name, initialValue))
}
render(){
const {
initialValue,
component: Compo,
...fieldProps
} = this.props
return <Compo {...fieldProps} />
}
}
function reduxFormInputContainer(component){
return function(props){
return <ReduxFormInputContainer {...props} component={component} />
}
}
export default reduxFormInputContainer
and then for exemple:
import reduxFormInputContainer from 'app/lib/reduxFormInputContainer'
InputNumericWidget = reduxFormInputContainer(InputNumericWidget)
class InputNumeric extends Component{
render(){
const props = this.props
return (
<Field component={InputNumericWidget} {...props} />
)
}
}