ReactJS Semantic ui - Cannot type into form when value attribute exists - forms

I am following a ReactJS tutorial to set up a login form. Semantic ui is used and imported. The email and password are passed into the value attribute inside the form. When this happens, I cannot type anything into the form. As soon as I remove it, I can type information in but I assume it won't get passed into anywhere.
Cannot seem to find this issues anywhere else. Has anyone experienced this issue before?
import React from 'react';
import PropTypes from 'prop-types';
import { Form, Button } from 'semantic-ui-react';
import Validator from 'validator';
import InlineError from '../messages/InlineError';
class LoginForm extends React.Component {
state = {
data: {
email: "",
password: ""
},
loading: false,
errors: {}
};
//... is called spread
onChange = e => this.setState({
data: {...this.state.data, [e.target.name]: e.target.value }
});
//() means function takes no params
onSubmit = () => {
const errors = this.validate(this.state.data);
this.setState({errors}); //if there are errors, display them
if(Object.keys(errors).length === 0){
this.props.submit(this.state.data);
}
};
validate = (data) => {
const errors = {};
if(!Validator.isEmail(data.email))
errors.email = "Invalid email";
if(!data.password)
errors.password = "Can't be blank";
return errors;
};
render() {
const { data, errors } = this.state; // import variables into html
return (
<div>
<Form onSubmit={ this.onSubmit }>
<Form.Field error={!!errors.email}>
<label htmlFor="email">Email</label>
<input type="email"
id="email"
placeholder="example#abc.com"
value={ data.email }
onChange={ this.onChange }/>
{errors.email && <InlineError text={errors.email}/>}
</Form.Field>
<Form.Field error={!!errors.email}>
<label htmlFor="password">Password</label>
<input type="password"
id="password"
value={ data.password }
onChange={this.onChange}/>
{errors.password && <InlineError text={errors.password}/>}
</Form.Field>
<Button primary>Login</Button>
</Form>
</div>
);
}
}
LoginForm.propTypes = {
submit: PropTypes.func.isRequired
};
export default LoginForm;
tutorial: https://www.youtube.com/watch?v=NO2DaxhoWHk&t=879s

onChange = e => this.setState({
data: {...this.state.data, [e.target.name]: e.target.value }
});
This function is setting the state to a variable that shares the name of your input field. Hence e.target.name. But your input fields do not have a name attribute.
You can fix that with:
import React from 'react';
import PropTypes from 'prop-types';
import { Form, Button } from 'semantic-ui-react';
import Validator from 'validator';
import InlineError from '../messages/InlineError';
class LoginForm extends React.Component {
state = {
data: {
email: "",
password: ""
},
loading: false,
errors: {}
};
//... is called spread
onChange = e => this.setState({
data: {...this.state.data, [e.target.name]: e.target.value }
});
//() means function takes no params
onSubmit = () => {
const errors = this.validate(this.state.data);
this.setState({errors}); //if there are errors, display them
if(Object.keys(errors).length === 0){
this.props.submit(this.state.data);
}
};
validate = (data) => {
const errors = {};
if(!Validator.isEmail(data.email))
errors.email = "Invalid email";
if(!data.password)
errors.password = "Can't be blank";
return errors;
};
render() {
const { data, errors } = this.state; // import variables into html
return (
<div>
<Form onSubmit={ this.onSubmit }>
<Form.Field error={!!errors.email}>
<label htmlFor="email">Email</label>
<input type="email"
id="email"
name="email"
placeholder="example#abc.com"
value={ data.email }
onChange={ this.onChange }/>
{errors.email && <InlineError text={errors.email}/>}
</Form.Field>
<Form.Field error={!!errors.email}>
<label htmlFor="password">Password</label>
<input type="password"
id="password"
name="password"
value={ data.password }
onChange={this.onChange}/>
{errors.password && <InlineError text={errors.password}/>}
</Form.Field>
<Button primary>Login</Button>
</Form>
</div>
);
}
}
LoginForm.propTypes = {
submit: PropTypes.func.isRequired
};
export default LoginForm;

Related

values.map is not a function Sequelize

I'm trying to send a form to my db but I get this error :
The problem XD
I have this problem only when I try to do it from the front, because when I try with Insomnia it works. So I'm not sure where the problem is coming from.
I'm using multer for the image.
The model:
const { DataTypes } = require('sequelize');
// Exportamos una funcion que define el modelo
// Luego le injectamos la conexion a sequelize.
module.exports = (sequelize) => {
// defino el modelo
sequelize.define('Recipe', {
id:{
type: DataTypes.UUID(5),
primaryKey:true,
defaultValue:DataTypes.UUIDV4(5)
},
title: {
type: DataTypes.STRING,
allowNull: false,
},
summary:{
type:DataTypes.STRING,
allowNull: false,
},
healthScore:{
type: DataTypes.INTEGER,
},
steps:{
type:DataTypes.ARRAY(DataTypes.STRING)
},
dishTypes:{
type:DataTypes.ARRAY(DataTypes.STRING)
},
readyInMinutes:{
type: DataTypes.INTEGER,
get(){
return "Ready in " + this.getDataValue("readyInMinutes") + " minutes"
}
},
ingredients:{
type: DataTypes.ARRAY(DataTypes.STRING)
},
servings:{
type:DataTypes.STRING
},
image:{
type:DataTypes.STRING,
defaultValue: `https://post.healthline.com/wp-content/uploads/2020/08/food-still-life-salmon-keto-healthy-eating-732x549-thumbnail-732x549.jpg`
}
},{
timestamps: false
});
};
The post route (actually the helper):
const createNewRecipe = require("../helpers/RecipeCont/CreateRecipe/CreateRecipe")
const createNewRecipeRoute = async (req, res) => {
try {
const data = {
title,
summary,
healthScore,
steps,
dishTypes,
readyInMinutes,
ingredients,
servings,
Diet_type,
} = req.body;
const image = req.file.path
let newRecipe = await createNewRecipe({
title,
summary,
healthScore,
steps,
dishTypes,
readyInMinutes,
ingredients,
servings,
image,
});
await newRecipe.addDiet_type(Diet_type)
console.log(req.file)
res.status(200).json("Receta creada con éxito");
} catch (error) {
console.log(error)
res.status(400).json(error.message);
}
}
module.exports = createNewRecipeRoute;
The form
import React, {useState} from "react";
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { createRecipe, getDietTypes } from "../../actions/actions";
import styles from "./form.module.css"
export default function Form(){
const [form,setForm] = useState({
title:"",
summary:"",
healthScore:0,
steps:[],
dishTypes:[],
readyInMinutes:0,
ingredients:[],
servings:0,
image:"",
Diet_type:[1]
})
const [steps, setSteps] = useState("")
const [dishTypes, setDishType]=useState("")
const [ingredients, setIngredients]= useState("")
const dispatch=useDispatch()
useEffect(()=>{
dispatch(getDietTypes())
},[])
const diets = useSelector(state=> state.diet)
const changeHandler=(e)=>{
if(e.target.name==="image"){
setForm({...form,[e.target.name]: e.target.file })
}
setForm({...form, [e.target.name]:e.target.value})
}
const stepHandler = (e)=>{
let aux = e.target.name
let auxV = e.target.value
if(e.key==="Enter"){
e.preventDefault()
setForm({...form, [e.target.name]: [...form[aux] , auxV ]})
aux==="steps"? setSteps("") : aux==="ingredients"? setIngredients("") : setDishType("")
}
console.log(form)
}
const deleteHandler = (e)=>{
let help = e.target.name
e.preventDefault()
let aux = form[help].filter(s=> s!==e.target.value)
setForm({...form, [help]: [...aux]})
}
const imageHandler = (e)=>{
setForm({...form, [e.target.name]:e.target.files[0]})
}
const sendHandler = (e)=>{
e.preventDefault();
if(form.image!==""){
const formData = new FormData()
formData.append("image",form.image)
formData.append("title",form.title)
formData.append("summary",form.summary)
formData.append("healthScore",form.healthScore)
formData.append("steps",form.steps)
formData.append("dishTypes",form.dishTypes)
formData.append("readyInMinutes",form.readyInMinutes)
formData.append("Ingredients",form.ingredients)
formData.append("servings",form.servings)
formData.append("Diet_type",form.Diet_type)
for (var key of formData.entries()) {
console.log(key[0] + ', ' + key[1]);
}
dispatch(createRecipe(formData))
} else {
dispatch(createRecipe(form))
}
console.log(form)
}
return(
<>
<div className={styles.div} >
<h2>Create your own recipe!</h2>
<form encType="multipart/form-data" method="POST" onSubmit={sendHandler}>
<div className={styles.divTitle}>
<h2>Title:</h2>
<input type="text" placeholder="Title" name="title" value={form.title} onChange={changeHandler}></input>
</div>
<input type="text" placeholder="summary" name="summary" value={form.summary} onChange={changeHandler}></input>
{form.healthScore}<input type="range" placeholder="healthScore" name="healthScore" min="1" max="100" step="1" value={form.healthScore} onChange={changeHandler} ></input>
<div>
<input type="text" name="steps" value={steps} onChange={(e)=>{setSteps(e.target.value)}} onKeyDown={stepHandler} placeholder="Steps"></input>
<div>
{form.steps.length>0? form.steps.map(e=><li>{e}<button value={e} name="steps" onClick={deleteHandler}>x</button></li>) : "Add the steps of your recipe!"}
</div>
</div>
<div>
<input type="text" name="ingredients" value={ingredients} onChange={(e)=>{setIngredients(e.target.value)}} onKeyDown={stepHandler} placeholder="Ingredients"></input>
<div>
{form.ingredients.length>0? form.ingredients.map(e=><li>{e}<button value={e} name="ingredients" onClick={deleteHandler}>x</button></li>) : "Add the ingredients of your recipe!"}
</div>
</div>
<div>
<input type="text" name="dishTypes" value={dishTypes} onChange={(e)=>{setDishType(e.target.value)}} onKeyDown={stepHandler} placeholder="Dish types"></input>
<div>
{form.dishTypes.length>0? form.dishTypes.map(e=><li>{e}<button value={e} onClick={deleteHandler}>x</button></li>) : "Add the dish types of your recipe!"}
</div>
</div>
<select>
{console.log(diets)}
{diets?.map(d=><option key={d.id} id={d.id}>{d.name}</option>)}
</select>
<input className={styles.number} name="readyInMinutes" value={form.readyInMinutes} onChange={changeHandler} type="number"></input>
<input className={styles.number} name="servings" value={form.servings} onChange={changeHandler} type="number"></input>
<input type="file" name="image" onChange={imageHandler}></input>
<input type="submit"></input>
</form>
</div>
</>
)
}
I'm still working on the form, in Diet_type for example, but even trying to hardcode the state to make the post it doesn't work.
The "for" is because the console.log doesn't work with formData and at the beginning I thought it was that I wasn't sending anything, but actually I do.
I save the image of all the request even for those which can fulfill so the middleware seems its working too.
I hope you can help me to understand what's going on and try to find a solution, c: Thanks for your time!!
The object values does not have the map methode.
You expect that there should be a map methode, but this is not a javascipt array with protype map. So you get the error.
You can use the loadash library.
const {map} require(`lodash`)
map(values, value => {
console.log(value)
// your code coes here
}

How to prevent repeating code of form validation

I created the form for multiple inputs, where the specific input data shall be validated at the time of data entry and once again for all data just before the submission of the form to the backend.
The conditions to submit: all fields are mandatory and the data is valid.
My program works, but I don't like that I'm repeating the validation code in 2 places: in ErrorOutput and hadleSubmit.
In ErrorOutput I check the data and, if necessary, display an error message.
In handleSubmit I just check the data without displaying of error message and if all data is valid, I confirm submitting.
How can I improve my example to prevent the repetition of this code, but the data validation was also at the time of data entry and before submission?
import React from 'react'
import { render } from 'react-dom'
const ErrorOutput = props => {
let name = props.name
let inputValue = props.case
let submit = props.submit
// Data validation
if (name === 'firstName') {
if (!inputValue.match(/^[a-zA-Z]+$/) && inputValue.length > 0) {
return <span>Letters only</span>
} else if (submit && inputValue.length === 0) {
return <span>Required</span>
}
return <span></span>
}
if (name === 'telNo') {
if(!inputValue.match(/^[0-9]+$/) && inputValue.length > 0) {
return <span>Numbers only</span>
} else if (submit && inputValue.length === 0) {
return <span>Required</span>
}
return <span></span>
}
}
class App extends React.Component {
constructor(props){
super(props)
this.state = {
firstName: '',
telNo: '',
submit: false
}
}
handleSubmit(e){
e.preventDefault()
let submit = true
let error = true
const { firstName, telNo } = this.state
this.setState ({submit: submit})
// Repeat the data validation before submission
if (firstName === '' || !firstName.match(/^[a-zA-Z]+$/)) {
error = true
} else if (telNo === '' || !telNo.match(/^[0-9]+$/)) {
error = true
} else {
error = false
}
// Submited if all data is valid
if (!error) {
// send data
return alert('Success!')
}
}
handleValidation(e) {
this.setState({
[e.target.name]: e.target.value
})
}
render() {
return (
<form onSubmit={this.handleSubmit.bind(this)}>
<div>
<label>
First name:
</label>
<input
type='text'
name ='firstName'
value = {this.state.firstName}
onChange = {this.handleValidation.bind(this)}
/>
<ErrorOutput case={this.state.firstName} name={'firstName'} submit = {this.state.submit} />
</div>
<div>
<label>
Phone number:
</label>
<input
type='tel'
name ='telNo'
value = {this.state.telNo}
onChange = {this.handleValidation.bind(this)}
/>
<ErrorOutput case={this.state.telNo} name={'telNo'} submit = {this.state.submit} />
</div>
<button>
Submit
</button>
</form>
)
}
}
render(
<App />,
document.getElementById('root')
)
You could extract a FormItem component:
class FormItem extends React.Component {
render() {
return (
<div>
<label>
{this.props.label}
</label>
<input
{...this.props.input}
/>
<ErrorOutput
case={this.props.input.value}
name={this.props.input.name}
submit={this.props.onSubmit}
/>
</div>
);
}
}
and use it in your App:
render() {
return (
<form onSubmit={this.handleSubmit.bind(this)}>
<FormItem label='First name:' input={{
type: 'text'
name: 'firstName'
value: this.state.firstName,
onChange: this.handleValidation.bind(this)
}}
onSubmit={this.state.submit}
/>
<FormItem label='Phone number:' input={{
type:'tel'
name :'telNo'
value : {this.state.telNo}
onChange : {this.handleValidation.bind(this)}
}}
onSubmit={this.state.submit}
/>
<button>
Submit
</button>
</form>
)
}
this is where libraries like react-final-form and redux-form become handy.
UPD
ErrorOutput component should not validate anything, it is not a responsibility of a component. Instead, you could validate your values on inputs blur event and before submit:
class App extends React.Component {
constructor(props){
super(props)
this.state = {
firstName: '',
telNo: '',
submit: false,
errors: {},
invalid: false,
}
}
handleSubmit(e){
e.preventDefault()
if (this.validate()) {
// handle error
} else {
// submit
}
}
validate = () => {
const { firstName, telNo } = this.state
const errors = {}
let invalid = false;
if (firstName === '' || !firstName.match(/^[a-zA-Z]+$/)) {
errors.firstName = 'first name is required'
invalid = true;
} else if (telNo === '' || !telNo.match(/^[0-9]+$/)) {
telNo.telNo = 'telNo is required'
invalid = true;
}
this.setState({
invalid,
errors,
})
return invalid;
}
render() {
return (
<form onSubmit={this.handleSubmit.bind(this)}>
<FormItem label='First name:' input={{
type: 'text',
name: 'firstName',
value: this.state.firstName,
onChange: e => this.setState({ firstName: e.target.value }),
onBlur: () => this.validate(),
}}
/>
<FormItem label='Phone number:' input={{
type: 'tel',
name: 'telNo',
value: this.state.telNo,
onChange: e => this.setState({ telNo: e.target.value }),
onBlur: () => this.validate(),
}}
/>
<button>
Submit
</button>
</form>
)
}
}
and FormItem and ErrorOutput:
const ErrorOutput = ({ error }) => <span>{error}</span>
class FormItem extends React.Component {
render() {
return (
<div>
<label>
{this.props.label}
</label>
<input
{...this.props.input}
/>
{this.props.error && <ErrorOutput error={this.props.error} />}
</div>
);
}
}
const ErrorOutput = ({ errorText }) => <span>{errorText}</span>;
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
firstName: "",
telNo: "",
submit: false,
errors: {} //Add errors object to the state.
};
}
handleSubmit(e) {
e.preventDefault();
const errors = this.validateData();
if (Object.keys(errors).length === 0) {
alert("Success");
}
//else errors exist
this.setState({ errors });
}
validateData = () => {
let errors = {};
const { firstName, telNo } = this.state; // read the values to validate
if (firstName.length === 0) {
errors.firstName = "Required";
} else if (firstName.length > 0 && !firstName.match(/^[a-zA-Z]+$/)) {
errors.firstName = "Letters only";
}
if (telNo.length === 0) {
errors.telNo = "Required";
} else if (telNo.length > 0 && !telNo.match(/^[0-9]+$/)) {
errors.telNo = "Numbers only";
}
return errors;
};
handleValidation(e) {
this.setState({
[e.target.name]: e.target.value
});
}
render() {
const { errors } = this.state; // read errors from the state
return (
<form onSubmit={this.handleSubmit.bind(this)}>
<div>
<label>First name:</label>
<input
type="text"
name="firstName"
value={this.state.firstName}
onChange={this.handleValidation.bind(this)}
/>
{errors.firstName && <ErrorOutput errorText={errors.firstName} />}
</div>
<div>
<label>Phone number:</label>
<input
type="tel"
name="telNo"
value={this.state.telNo}
onChange={this.handleValidation.bind(this)}
/>
{errors.telNo && <ErrorOutput errorText={errors.telNo} />}
</div>
<button>Submit</button>
</form>
);
}
}
render(<App />, document.getElementById("root"));

Load form value from state

First I show a list of transactions, when a user selects a single transaction a new page is opened with the transaction ID in the URL. On this page are details of the transaction displayed.
The code below is just the details page. It shows all the right details.
One of the details is a list of 0 or more tags, I'd like to be able to edit the list of tags and save the result.
At this point, I always end up with a clean Input field and I do not understand how to populate this field with the existing transaction['tags'] data.
It seems that the transaction['tags'] is not initialized until the page is rendered, I cannot use it in the constructor or in the componentDidMount.
What I expect is that the transaction object as stated in the mapStateToProps is available and I can change the line in the constructor from: this.state = {value: ''}; to this.state = {value: transaction['tags']}
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { fetchTransaction } from '../actions';
class TransactionsIndex extends Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
componentDidMount() {
const { _key } = this.props.match.params;
this.props.fetchTransaction(_key);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
const { transaction } = this.props;
if (!transaction) {
return <div>Loading...</div>;
}
let tags = null;
tags =
<div>
<form onSubmit={this.handleSaveTagsclick}>
<input type="text" value={this.state.value} onChange={this.handleChange} />
<input type="submit" value="Submit" />
</form>
</div>
// console.log(transaction['tags']);
return (
<div className="container">
<div className="well">
<div>Transactiedatum: { transaction["Transactiedatum"] }</div>
<div>Valutacode: { transaction["Valutacode"] }</div>
<div>CreditDebet: { transaction["CreditDebet"] }</div>
<div>Bedrag: { transaction["Bedrag"] }</div>
<div>Tegenrekeningnummer: { transaction["Tegenrekeningnummer"] }</div>
<div>Tegenrekeninghouder: { transaction["Tegenrekeninghouder"] }</div>
<div>Valutadatum: { transaction["Valutadatum"] }</div>
<div>Betaalwijze: { transaction["Betaalwijze"] }</div>
<div>Omschrijving: { transaction["Omschrijving"] }</div>
<div>Type betaling: { transaction["Type betaling"] }</div>
<div>Machtigingsnummer: { transaction["Machtigingsnummer"] }</div>
<div>Incassant ID: { transaction["Incassant ID"] }</div>
<div>Adres: { transaction["Adres"] }</div>
<div>Status: { transaction["status"] }</div>
<div>Created: { transaction["created"] }</div>
{tags}
</div>
<Link to="/"><button type="button" className="btn btn-default">Back</button></Link>
</div>
);
};
}
function mapStateToProps({ transactions }) {
// console.log('transactions_selectedTransaction: ' + transactions['selectedTransaction']);
return { transaction: transactions['selectedTransaction'] };
}
export default connect(mapStateToProps, { fetchTransaction })(TransactionsIndex);
I found this but it did not help me: Redux-form: Set form values from state
and this: How to get state / value from form component?

React/Redux clear form elements values after submit

I have problems with clearing values from input and select form elements in react form after successful submit through axios library. Just want to mention that i do not use redux-form.
I don't know if I am on the right track here, this is my workflow by far: I wrote a form with react-bootstrap, give every input and select value through props and I access and update the state through these props. I have wrote actions and reducers for updating input values, and one action is dispatched in my component, but the second action and the reducer that is supposed to clear values after submit doesn't work as expected. This is the main problem, I'm not sure if I dispatch FORM_RESET action form in the right place, because I call it in the action that is responsible for posting data to server, and on success callback I dispatch FORM_RESET.
Below is the code relevant for this problem.
/* actionRegister.js */
let _registerUserFailure = (payload) => {
return {
type: types.SAVE_USER_FAILURE,
payload
};
};
let _registerUserSuccess = (payload) => {
return {
type: types.SAVE_USER_SUCCESS,
payload,
is_Active: 0,
isLoading:true
};
};
let _hideNotification = (payload) => {
return {
type: types.HIDE_NOTIFICATION,
payload: ''
};
};
//asynchronous helpers
export function registerUser({ //use redux-thunk for asynchronous dispatch
timezone,
password,
passwordConfirmation,
email,
name
}) {
return dispatch => {
axios.all([axios.post('/auth/signup', {
timezone,
password,
passwordConfirmation,
email,
name,
is_Active: 0
})
// axios.post('/send', {email})
])
.then(axios.spread(res => {
dispatch(_registerUserSuccess(res.data.message));
dispatch(formReset()); //here I dispatch clearing form data
setTimeout(() => {
dispatch(_hideNotification(res.data.message));
}, 10000);
}))
.catch(res => {
dispatch(_registerUserFailure(res.data.message)); //BE validation and passport error message
setTimeout(() => {
dispatch(_hideNotification(res.data.message));
}, 10000);
});
};
}
/* actionForm.js */
//synchronous action creators
export function formUpdate(name, value) {
return {
type: types.FORM_UPDATE_VALUE,
name, //shorthand from name:name introduced in ES2016
value
};
}
export function formReset() {
return {
type: types.FORM_RESET
};
}
/* reducerRegister.js */
const INITIAL_STATE = {
error:{},
is_Active:false,
isLoading:false
};
const reducerSignup = (state = INITIAL_STATE , action) => {
switch(action.type) {
case types.SAVE_USER_SUCCESS:
return { ...state, is_Active:false, isLoading: true, error: { register: action.payload }};
case types.SAVE_USER_FAILURE:
return { ...state, error: { register: action.payload }};
case types.HIDE_NOTIFICATION:
return { ...state , error:{} };
}
return state;
};
export default reducerSignup;
/* reducerForm.js */
const INITIAL_STATE = {
values: {}
};
const reducerUpdate = (state = INITIAL_STATE, action) => {
switch (action.type) {
case types.FORM_UPDATE_VALUE:
return Object.assign({}, state, {
values: Object.assign({}, state.values, {
[action.name]: action.value,
})
});
case types.FORM_RESET:
return INITIAL_STATE;
//here I need isLoading value from reducerRegister.js
}
return state;
};
export default reducerUpdate;
/* SignupForm.js */
import React, {Component} from 'react';
import {reduxForm} from 'redux-form';
import {connect} from 'react-redux';
import map from 'lodash/map';
import timezones from '../../data/timezones';
import styles from '../formElements/formElements.scss';
import {registerUser} from '../../actions/actionRegister';
import {formUpdate} from '../../actions/actionForm';
import FieldGroup from '../formElements/FieldGroup';
import { Form, FormControl, Col, Checkbox, Button, FormGroup } from 'react-bootstrap';
// {... props} passing large number of props wrap in object with spread notation
class SignupForm extends Component { //if component have state it needs to be class
constructor(props) {
super(props);
this.state = {
errors: { //this errors are irrelevant for now
name:'',
email: '',
password: '',
passwordConfirmation:'',
timezone:''
},
};
}
onChange = (event, index, value) => {
this.props.onChange(event.target.name, event.target.value);
};
onSave = (event) => {
event.preventDefault();
this.props.onSave(this.props.values);
}
render() {
let isLoading = this.props.isLoading;
return (
// this.props.handleSubmit is created by reduxForm()
// if the form is valid, it will call this.props.onSubmit
<Form onSubmit={this.onSave} horizontal>
<FieldGroup
id="formControlsName"
type="text"
label="Name"
name="name"
placeholder="Enter Name"
value={this.props.values[name]}
onChange={this.onChange}
help={this.state.errors.name}
/>
<FieldGroup
id="formControlsEmail"
type="text"
label="Email"
name="email"
placeholder="Enter Email"
value={this.props.values[name]}
onChange={this.onChange}
help={this.state.errors.email}
/>
<FieldGroup
id="formControlsPassword"
type="password"
label="Password"
name="password"
placeholder="Enter Password"
value={this.props.values[name]}
onChange={this.onChange}
help={this.state.errors.password}
/>
<FieldGroup
id="formControlsPasswordConfirmation"
type="password"
label="Password Confirmation"
name="passwordConfirmation"
placeholder="Enter Password"
value={this.props.values[name]}
onChange={this.onChange}
help={this.state.errors.passwordConfirmation}
/>
<FieldGroup
id="formControlsTimezone"
label="Time Zone"
name="timezone"
placeholder="Select Time Zone"
componentClass="select"
defaultValue="Select Your Timezone"
value={this.props.values[name]}
onChange={this.onChange}
help={this.state.errors.timezone}
>
<option value="Select Your Timezone">Select Your Timezone</option>
{
map(timezones, (key, value) =>
<option key={key} value={key}>{value}</option>)
}
</FieldGroup>
<FormGroup>
<Col smOffset={4} sm={8}>
<Checkbox>Remember me</Checkbox>
</Col>
</FormGroup>
<FormGroup>
<Col smOffset={4} sm={8}>
<Button type="submit" disabled={isLoading}
onClick={!isLoading ? isLoading : null}
>
{ isLoading ? 'Creating...' : 'Create New Account'}
</Button>
</Col>
</FormGroup>
{this.props.errorMessage && this.props.errorMessage.register &&
<div className="error-container">{this.props.errorMessage.register}</div>}
</Form>
//this.setState({ disabled: true });
//this.props.errorMessage.register == this.props = {errorMessage :{ register: ''}}
);
}
}
function mapStateToProps(state) {
return {
errorMessage: state.signup.error,
isLoading: state.signup.isLoading,
values: state.form.values
};
}
function mapDispatchToProps(dispatch) {
return {
onSave: (values) => dispatch(registerUser(values)),
onChange: (name, value) => dispatch(formUpdate(name, value))
};
}
export default connect(mapStateToProps, mapDispatchToProps)(SignupForm)
;
There is no need to use redux-form :-) You're on the right path and you're calling FORM_RESET action in the right place.
Couple of things:
are you sure you are importing formReset in actionRegister.js?
in reducerForm I would suggest to still return new state here:
case types.FORM_RESET:
return { ...INITIAL_STATE }; // or Object.assign({}, INITIAL_STATE)
And btw. why are you setting isLoading: true on success? I would suggest to create 3 actions instead of 2:
SAVE_USER_START (which you dispatch before sending a request),
set isLoading to true,
SAVE_USER_SUCCESS - set isLoading to false
SAVE_USER_FAILURE - set isLoading to false
I would suggest to look into redux-form library. It provides configuration option to clear fields after submit out of the box.

unable set a default value in redux-form w. react

I'm unable to set a default value of a form w/redux-form. The result I'm looking for is an editable text field to later to submit to the database. i.e. updating an email address.
I've tried setting a property in the form to value or defaultValue.
Note: I've taken repetitive code out to make this easier to read with just a "name" field.
any insight is appreciated!
import React, { Component, PropTypes } from 'react';
import { reduxForm } from 'redux-form';
export const fields = [ 'name']
//(container) page-profile.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Profile from '../components/Profile';
class PageProfile extends Component {
render() {
return (
<Profile
userInfo = {this.props.userInfo}
/>
)
}
}
// requiring this page before rendering -- breaks page
PageProfile.propTypes = {
//userInfo: PropTypes.object.isRequired
}
function mapStateToProps(state) {
return {
userInfo : state.auth.userInfo
}
}
// injection to child
export default connect(mapStateToProps, {
})(PageProfile);
// profile.js
export default class Profile extends Component {
render() {
const { fields: {name }, resetForm, handleSubmit, submitting } = this.props
return (
<div>
<img className="image" src={this.props.userInfo.img_url}/>
<form onSubmit={handleSubmit}>
<div>
<div>
<label>name</label>
<div>
<input type="text" defaultValue={this.props.userInfo.name} placeholder="name" {...name}/>
</div>
{name.touched && name.error && <div>{name.error}</div>}
</div>
<button type="submit" disabled={submitting}>
{submitting ? <i/> : <i/>} Submit
</button>
</form>
</div>
)
}
}
Profile.propTypes = {
fields: PropTypes.object.isRequired,
handleSubmit: PropTypes.func.isRequired,
resetForm: PropTypes.func.isRequired,
submitting: PropTypes.bool.isRequired
}
export default reduxForm({
form: 'Profile',
fields,
validate
})(Profile)
You can supply initialValues in reduxForm's mapStateToProps:
const mapFormToProps = {
form: 'Profile',
fields,
validate
};
const mapStateToProps = (state, ownProps) => ({
initialValues: {
name: ownProps.userInfo.name
}
});
reduxForm(
mapFormToProps,
mapStateToProps
)(Profile)
Then just bind like so: <input type="text" placeholder="name" {...name} />.