I am working on a login form where each input is created dynamically as a field.
This is my Login.js file:
import _ from 'lodash';
import React, { Component } from 'react';
import {reduxForm, Field } from 'redux-form';
import{ Link } from 'react-router-dom';
import FIELDS from './loginFields';
import LoginField from './LoginField'
import { connect } from 'react-redux';
import * as actions from '../../actions'
class LoginForm extends Component {
constructor(){
super();
this.state={
username: '',
password: ''
};
};
handleChange = (e)=>{
this.setState({username: e.target.value, password: e.target.value});
};
renderFields(){
return _.map(FIELDS, ({ label, name, type })=> {
return <Field onChange={this.handleChange} className='purple-text' key={name} component={ LoginField } type={type} label={label} name={name} />
});
};
render(){
const { username, password } = this.state;
const isEnabled = username.length > 0 && password.lenth>7;
return (
<div className='valign-wrapper row login-box' style={{marginTop:'100px'}}>
<div className='col card hoverable s10 pull-s1 m6 pull-m3 l4 pull-l4'>
<form method='POST' action='/api/login'>
<div className = 'card-content'>
<span className='card-title purple-text' style={{textAlign:'center'}}>Login<a href='/register'> Not a member? sign up!</a></span>
<div className='center-align row'>
<li key='google' style={{marginLeft: '30px'}} className='col m6 center-align white-text darken-3'><a className='white-text' href='/auth/google'><img alt="" src="https://img.icons8.com/cute-clipart/64/000000/google-logo.png"/></a></li>
<li key='facebook' className='col center-align white-text darken-3'><a className='white-text' href='/auth/facebook'><img alt = "" src="https://img.icons8.com/cute-clipart/64/000000/facebook-new.png"/></a></li>
</div>
<div className='row input-field col s12'>
{this.renderFields()}
<Link to='/' className='purple btn-flat left white-text'>Back</Link>
<button disabled={!isEnabled} type='submit' className='purple btn-flat right white-text'>Login
<i className='material-icons right'>done</i>
</button>
</div>
</div>
</form>
</div>
</div>
);
};
};
function validate(values){
const errors = {};
_.each(FIELDS, ({name})=>{
if(!values[name]){
errors[name] = 'You must enter a value!'
}
});
return errors;
};
const form = reduxForm({
validate,
form: 'LoginForm'
});
export default connect(null, actions)(form(LoginForm));
Here is loginFields.js
export default
[
{ label: 'Username', name: 'username', type: 'text'},
{ label: 'Password', name: 'password', type: 'password'}
];
and here is LoginField.js
import React from 'react';
export default ({ input, label, type, meta })=>{
return(
<div>
<label className='purple-text'>{label}</label>
<input {...input} type={type} style= {{marginBottom: '5px'}}/>
<div className = "red-text" style={{ marginBottom: '20px'}}>
{meta.touched && meta.error}
</div>
</div>
);
};
I am having trouble properly setting onChange and my constructor to disable the login button until all fields are filled. I have been able to disable the button until a single input has started to be filled in, not disabled at all, and not enabled at all. but have not been able to achieve the desired outcome.
I have tried using lodash to map over each field grabbing values by the input name property, and moving functions around.
Any help would be greatly appreciated, if i can provide any more information for this question please let me know.
2
Answers
just adding this as an answer in case anyone else comes across this issue.
after tons of digging I finally found documentation. in redux form has a built in prop called {invalid} which checks against the validate function. instead of messing with state all i had to do was add
inside the render method. constructor and handle change and onChange were no longer necessary.. then.
The initial problem I see is the onChange function will update state for both password and username whenever either of them is changed. The function takes the event and does not distinguish as to which input is the target. You can pass an additional parameter from the Field that includes the field name, or you can check the target’s id or something so you know which input’s state should be updated.
In LoginForm
You also need to pass the onChange callback down to the actual input in LoginField.js
Here’s a codeSandbox.