In my redux-form I'm having troubles showing the errors back to the form,
here's my form:
import React, { Component } from "react";
import { connect } from "react-redux";
import {
registerUser,
signOutUser
} from "../../actions/redux-token-auth-config";
import { Field, reduxForm, SubmissionError } from "redux-form";
import { FormDiv, SubmitButton, Title, Working } from "./styles";
const renderField = ({ input, label, type, meta: { touched, error } }) =>
<div>
<label>
{label}
</label>
<div>
<input {...input} placeholder={label} type={type} />
{touched &&
error &&
<span>
{error}
</span>}
</div>
</div>;
const errorsFrom = response => response.response.data;
const signUp = (data, dispatch, props) => {
return dispatch(props.registerUser(data))
.then(response => dispatch(signOutUser(data)))
.catch(response => {
throw new SubmissionError(errorsFrom(response));
});
};
const SignUp = props => {
const { error, handleSubmit, pristine, submitting } = props;
return (
<FormDiv>
<Title>subscribe</Title>
<form onSubmit={handleSubmit(signUp)}>
<Field
name="email"
component={renderField}
type="text"
placeholder="Email"
label="Email"
/>
<br />
<Field
name="password"
component={renderField}
type="password"
placeholder="Password"
label="Password"
/>
<br />
<Field
name="passwordConfirmation"
component={renderField}
type="password"
placeholder="Confirm Password"
label="Confirm Password"
/>
<br />
{error &&
<strong>
{error}
</strong>}
<SubmitButton type="submit" disabled={pristine || submitting}>
Sign Up
</SubmitButton>
{submitting && <Working />}
</form>
</FormDiv>
);
};
const ReduxFormSignUp = reduxForm({
form: "signup"
})(SignUp);
const mapStateToProps = (state, props) => {
return {
error:
state.form.signup && state.form.signup.submitErrors
? state.form.signup.submitErrors.errors
: null
};
};
const mapDispatchToProps = dispatch => {
return {
registerUser
};
};
export default connect(mapStateToProps, mapDispatchToProps)(ReduxFormSignUp);
After submitting I get some errors in my redux state:
but for some reason they fail to be passed back in the props. so my props contain handleSubmit, pristine, submitting but no error prop. Regardless whether I use my mapStateToProps or not
Update
There seems to be an incongruence between the documentation and the library, I see no reference to passing errors to fields in here https://github.com/erikras/redux-form/blob/master/src/createField.js, as it's described here: https://redux-form.com/7.2.3/docs/api/field.md/#usage
Similarly here: https://redux-form.com/7.2.3/docs/api/reduxform.md/ I see no references to passing error prop to the wrapped component. But I'm seeing so many examples where I should expect an error prop passed by reduxForm.
Any clues?
Each field error is passed to the Field and the generic error (_) is passed to the form. I think your code should work. Are the fields touched?
Related
Let me tell you, this code were working fine and showing all products in shop page but after adding mapDispatchToProps.
it giving error:- TypeError: this.props.getProducts is not a function
mapStateToProps is giving products.
Trying to post data using mapDispatchToProps.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { getProducts } from '../actions/productsAction';
import { addToCart } from '../actions/cartAction';
class Shop extends Component {
constructor(props) {
super(props);
this.state = {
pid: '',
pname: '',
pprice: '',
pimg: '',
qty: '',
total_price: '',
getValue:[]
};
}
componentDidMount() {
this.props.dispatch(getProducts());
let getValue = localStorage.getItem("userData");
this.setState({
getValue: JSON.parse(getValue),
});
}
handleSubmit = (event) => {
event.preventDefault();
const pid = this.state.pid;
const pname = this.state.pname;
const pprice = this.state.pprice;
const pimg = this.state.pimg;
const qty = this.state.qty;
const total_price = this.state.total_price;
const email = this.state.getValue.email;
const CartData = {pid: pid, pname: pname, pprice: pprice, pimg: pimg, qty:qty, total_price:total_price}
this.props.addToCart(CartData);
};
render() {
return (
...html code
<form onSubmit={ this.handleSubmit }>
<input type="hidden" onChange={this.handleChange} name="pid" value={product._id} />
<input type="hidden" onChange={this.handleChange} name="pname" value={product.pname} />
<input type="hidden" onChange={this.handleChange} name="pprice" value={product.pprice} />
<input type="hidden" onChange={this.handleChange} name="qty" value="1" />
<input type="hidden" onChange={this.handleChange} name="pimage" value={product.pimg} />
<input type="hidden" onChange={this.handleChange} name="total_price" value={product.pprice} />
<button type="submit" class="pro-btn"><i class="icon-basket"></i></button>
</form>
...html code
const mapStateToProps = (state) => ({ products: state.products });
const mapDispatchToProps = { addToCart };
export default connect(mapStateToProps, mapDispatchToProps)(Shop);
You have not defined the mapDispatchToProps properly.
It should look like below,
const mapDispatchToProps = (dispatch) => {
return {
addToCart: () => dispatch(addToCart()),
getProducts: () => dispatch(getProducts())
}
};
And you should call the function directly using props
componentDidMount() {
this.props.getProducts();
let getValue = localStorage.getItem("userData");
this.setState({
getValue: JSON.parse(getValue),
});
}
You have two actions that you want to use in your component, so you'll want to include them both in your mapDispatchToProps object. The function notation described by #AmilaSenadheera will work, but the object shorthand is easier.
const mapDispatchToProps = {
addToCart,
getProducts
};
Then you should be able to call this.props.getProducts().
At the moment, I'm having difficulty populating the input fields for a redux form with initial values. Could someone please tell me what's wrong with the following code, or if this is a known issue? Thanks for the help and support.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Field, reduxForm, reset, initialize } from 'redux-form'
const renderField = props => (
<div>
<label>{props.placeholder}</label>
<div>
<input {...props}/>
{props.touched && props.error && <span>{props.error}</span>}
</div>
</div>
)
class EditArtist extends Component {
render() {
const { initialValues} = this.props;
console.log(initialValues)
return (
<form>
<Field name="name" component={renderField} type="text" placeholder="Name"/>
</form>
)
}
}
const validate = values => {
const errors = {};
return errors;
}
const mapStateToProps = (state) => ({
initialValues: {
name: "COOL"
}
});
export default connect(mapStateToProps)(reduxForm({
form: 'edit_artist_form',
validate,
enableReinitialize: true
})(EditArtist));
currently I'm trying to use redux-form/immutable in ma brand new project and I'm facing so problems which I can't resolve (aspecially, with this cool reducer.plugin), probably couse I'm kinda new to immutable and stuff like that.
Appreciating and advice or help on topic.
so, first of all:
import { combineReducers } from 'redux-immutable';
import routing from './routeReducer';
import { reducer as formReducer } from 'redux-form/immutable';
const rootReducer = combineReducers({
routing,
form: formReducer.plugin({
signupForm: (state, action) => {
switch(action.type) {
case 'SIGNUP_FACEBOOK':
return {
console.log(state);
}
default:
return state
}
}
})
});
while non of the forms field and touched this console.log(state) returns undefined, so here is the question: how may I initialize the form with some kind of state and actually update in on some sort of action ('SIGNUP_FACEBOOK' for this example)?
here is the form code sample:
import React, { PropTypes } from 'react';
import { Field, reduxForm } from 'redux-form/immutable';
import renderInputControl from '../../../common/InputControl';
import validate from './validate';
const SignUpForm = (props) => {
const { handleSubmit, submitting, invalid } = props;
return (
<form onSubmit={handleSubmit} className='signup__form'>
<Field name='name' type='text' component={renderInputControl} label='Full Name'/>
<Field name='email' type='email' component={renderInputControl} label='Email'/>
<Field name='password' type='password' component={renderInputControl} label='Password'/>
<Field name='tel' type='tel' component={renderInputControl} label='Phone Number'/>
<button type='submit' disabled={submitting || invalid}>Sign Up</button>
</form>
);
};
SignUpForm.propTypes = {
handleSubmit: PropTypes.func.isRequired,
onSubmit: PropTypes.func,
submitting: PropTypes.bool,
invalid: PropTypes.bool
};
export default reduxForm({
form: 'signupForm',
validate
})(SignUpForm);
and the second question is: even after I touch any of the fields (so it actually get some state, e.g. console.log(state) returns Map...), how should I update it, couse code:
state.getIn(['fields', 'name']).set('visited', false).set('touched', false)
which should set 'touched' and 'visited' to false doesn't do anything?
Best of luck!
I am looking to add reset password ability to an auth app in redux. The server sends out an email and puts a token in the params. I am trying to use a hidden field to store the param value that will need to be submitted back to the server. The problem is redux form doesn't seem to be picking up the value and is passing undefined. I am alittle new to redux form and know this isnt yet at v6. My plan is to get all the functionality for the auth built first then upgrade
import React, { Component } from 'react';
import { reduxForm } from 'redux-form';
import * as actions from '../../actions';
class Resetpassword extends Component {
componentWillMount() {
this.props.clearErrorMsg();
}
handleFormSubmit(formProps) {
// Call action creator to sign up the user!
this.props.resetPassword(formProps);
}
renderAlert() {
if (this.props.errorMessage) {
return (
<div className="alert alert-danger">
<strong>Oops!</strong> {this.props.errorMessage}
</div>
);
}
}
render() {
const { handleSubmit, fields: { password, passwordConfirm, token }} = this.props;
console.log(this.props.location.query.reset);
return (
<form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))}>
<fieldset className="form-group">
<label>Password:</label>
<input className="form-control" {...password} type="password" />
{password.touched && password.error && <div className="error">{password.error}</div>}
</fieldset>
<fieldset className="form-group">
<label>Confirm Password:</label>
<input className="form-control" {...passwordConfirm} type="password" />
{passwordConfirm.touched && passwordConfirm.error && <div className="error">{passwordConfirm.error}</div>}
</fieldset>
<input className="form-control" value={this.props.location.query.reset} {...token} type="hidden" />
{this.renderAlert()}
<button action="submit" className="btn btn-primary">Sign up!</button>
</form>
);
}
}
function validate(formProps) {
const errors = {};
if (!formProps.password) {
errors.password = 'Please enter a password';
}
if (!formProps.passwordConfirm) {
errors.passwordConfirm = 'Please enter a password confirmation';
}
if (formProps.password !== formProps.passwordConfirm) {
errors.password = 'Passwords must match';
}
return errors;
}
function mapStateToProps(state) {
return { errorMessage: state.auth.error };
}
export default reduxForm({
form: 'resetpassword',
fields: ['password', 'passwordConfirm','token'],
validate
}, mapStateToProps, actions)(Resetpassword);
The solution I found which may not follow best practices is to remove the hidden field and pass the query string prop into the action handleFormSubmit
handleFormSubmit(formProps) {
// Call action creator to sign up the user!
this.props.resetPassword(formProps,this.props.location.query.reset);}
Then having the action deal with it
I am new and would really appreciate your help. I have a form with four inputs (firstName, lastName, position and email) and want to pass the data the user puts in to the state to create an user object. But I always receive this error: Uncaught TypeError: Cannot read property 'firstName' of undefined
Maybe I did map the state wrong? I honestly don't know.
Here is my code:
The form I created which takes the users input:
import React, { Component } from 'react';
import TextField from 'material-ui/TextField';
import { Field, FieldArray, reduxForm} from 'redux-form';
import SelectField from 'material-ui/SelectField';
import MenuItem from 'material-ui/MenuItem';
import validate from './validate';
import injectTapEventPlugin from 'react-tap-event-plugin';
injectTapEventPlugin(); //Needed, otherwise an error message is shown in the console
//Texteingabefeld
const renderTextField = ({input, label, meta: {touched, error}, ...custom}) => (
<TextField
hintText={label}
floatingLabelText={label}
errorText={touched && error}
{...input}
{...custom}
/>
);
const renderUsers = ({fields, meta: { touched, error }}) => (
<div>
<div>
<button className="btn btn-primary"
type="button" onClick={() => fields.push({})}>
<span className="glyphicon glyphicon-plus-sign"/>Add User
</button>
{touched && error && <span>{error}</span>}
</div>
<Field name="firstName" component={renderTextField} label="First Name"/>
<Field name="lastName" component={renderTextField} label="Last Name"/>
<Field name="position" component={renderTextField} label="Position"/>
<Field name="email" component={renderTextField} label="Email"/>
{fields.map((user, index) =>
<div key={index}>
<Field name={`firstName${index}`} component={renderTextField} label="First Name"/>
<Field name={`lastName${index}`} component={renderTextField} label="Last Name"/>
<Field name={`position${index}`} component={renderTextField} label="Position"/>
<Field name={`email${index}`} component={renderTextField} label="Email"/>
<button className="btn btn-xs btn-danger"
type="button"
title="Remove User"
onClick={() => fields.remove(index)}>
<span className="glyphicon glyphicon-minus-sign"/>
</button>
</div>
)}
</div>
);
const UserCreation = props => {
const { handleSubmit, pristine, reset, submitting} = props;
return (
<form onSubmit={handleSubmit}>
<FieldArray name="users" component={renderUsers}/>
<div>
<button className="btn btn-primary btn-success"
type="submit"
disabled={pristine || submitting}>
<span className="glyphicon glyphicon-send" />
Submit
</button>
{' '}
<button type="button"
className="btn btn-primary btn-danger"
disabled={pristine || submitting}
onClick={reset}>
Cancel
</button>
</div>
</form>
);
}
export default reduxForm({
form: 'UserCreationForm',
validate
})(UserCreation);
The Component which fires the action when the user submits the form:
import React, {Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import UserCreation from '../components/UserCreation';
import { addUser } from '../actions/UserActions';
class UserControlPage extends Component {
componentWillMount() {
}
handleSubmit=(values) => {
addUser(values);
}
render() {
return (
<div>
<legend>
<span className="glyphicon glyphicon-user" aria-hidden="true"></span> User creation
</legend>
<UserCreation onSubmit={this.handleSubmit}/>
</div>
);
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({addUser}, dispatch);
}
function mapStateToProps(state) {
return {
users: state.users
}
}
export default connect(mapStateToProps, mapDispatchToProps)(UserControlPage);
The action creator:
import axios from 'axios';
import {ADD_USER} from './index';
export const addUser = (user) => {
return {
type: ADD_USER,
payload: {
// id: id,
firstName: user.payload.firstName,
lastName: user.payload.lastName,
position: user.payload.position,
email: user.payload.email
}
};
}
The reducer:
import {ADD_USER} from '../actions/index';
const INITIAL_STATE = {};
export default function UserReducer(state = INITIAL_STATE, action) {
switch (action.type) {
case ADD_USER:
return [...state, {
// id: action.id,
firstName: action.firstName,
lastName: action.lastName,
position: action.position,
email: action.email
}];
default:
return state;
}
}
The action creator has to get data without the payload, like this:
export const addUser = (user) => {
return {
type: ADD_USER,
payload: {
firstName: user.firstName,
lastName: user.lastName,
position: user.position,
email: user.email
}
}
}
I see a couple things wrong. First of all, you're attaching your data to a payload object inside of your action, but you're not reading from that object in your reducer. So in the reducer, for example, where you have action.firstName, it should actually be action.payload.firstName.
Secondly, what you're returning from the reducer is not right. Your initial state is an object, but you're returning an array. So your state is all kinds of messed up as soon as you mutate state. Try this instead:
import {ADD_USER} from '../actions/index';
const INITIAL_STATE = {};
export default function UserReducer(state = INITIAL_STATE, action) {
switch (action.type) {
case ADD_USER:
return {
...state,
user: action.payload
};
default:
return state;
}
}
Update
I see yet another problem. In this code, you're referencing user.payload, which does not exist.
export const addUser = (user) => {
return {
type: ADD_USER,
payload: {
// id: id,
firstName: user.payload.firstName,
lastName: user.payload.lastName,
position: user.payload.position,
email: user.payload.email
}
};
}
It should be user.firstName, user.lastName, etc.
Thanks guys. Changing the action creator to
export const addUser = (user) => {
return {
type: ADD_USER,
payload: {
firstName: user.firstName,
lastName: user.lastName,
position: user.position,
email: user.email
}
};
}
and the reducer to
const INITIAL_STATE = [];
export default function UserReducer(state = INITIAL_STATE, action) {
switch (action.type) {
case ADD_USER:
return [action.payload, ...state];
default:
return state;
}
}
solved the error.