Trigger redux form submit in componentWillReceiveProps - javascript

When a user submits a form, I'm checking to see if a certain field's value has changed. If it has, I'm displaying a confirmation modal. If the user clicks "Yes" in the modal, the modal component sets the "dismissedSubTypeChangeWarning" prop to true in redux.
Inside componentWillReceiveProps I listen for any changes to this prop. If it's value is true, I want to trigger the form's submission. I followed instructions at https://redux-form.com/6.5.0/examples/remotesubmit/ to configure this.
The dispatch call never fires, because in the console I see "detected dismiss" then nothing else. I expect to see "submitForm method" after "detected dismiss".
I condensed the code (below) to the most basic version to show the issue. When I run my code I see no errors in the console.
I found a solution using refs (see commented line), but faking a click doesn't seem like a good practice.
class ChangeEditForm extends React.Component {
componentWillReceiveProps (nextProps) {
if (nextProps.dismissedSubTypeChangeWarning) {
console.log('detected dismiss')
this.props.toggleDismissedSubTypeChangeWarning()
this.props.dispatch(submit('changeEdit'))
// this.refs.submit.click()
}
}
render () {
<form onSubmit={this.props.handleSubmit(this.props.submitForm)}>
<button type='submit' ref='submit'>Save</button>
</form>
}
}
const handlers = {
submitForm: props => (formValues) => {
console.log('submitForm method')
if ((formValues.subtype !== props.initialValues.subtype) && !props.dismissedSubTypeChangeWarning) {
console.log('show warning')
props.toggleSubTypeChangeConfirmModal()
} else {
console.log('submitting form')
}
}
}
export function mapStateToProps (state, props) {
return {
dismissedSubTypeChangeWarning: state.ui.dismissedSubTypeChangeWarning
}
}
export default compose(
pure,
reduxForm({
form: 'changeEdit',
onSubmit: handlers.submitForm
}),
connect(mapStateToProps, null),
withRouter,
withHandlers(handlers)
)(ChangeEditForm)

I still think you're over complicating your form submission.
As mentioned in the comments, the form submits once, the values are checked once, a popup spawns if values are different, confirming in the popup updates values, otherwise canceling closes modal and leaves form as is.
By creating a HOC we can control the modal and the form without having to resubmit the form on popup confirmation.
Working example: https://codesandbox.io/s/lxv9o1nxzl
container/ChangeNameHOC.js
import React, { Component } from "react";
import { connect } from "react-redux";
import { reset } from "redux-form";
import { updateUser } from "../actions";
import { Values } from "redux-form-website-template";
import ChangeNameForm from "./ChangeNameForm";
import ShowModal from "../components/ShowModal";
import CurrentStore from "../containers/CurrentStore";
class ChangeNameHOC extends Component {
state = {
modalIsOpen: false,
formProps: {}
};
openModal = () => this.setState({ modalIsOpen: true });
closeModal = () => this.setState({ modalIsOpen: false });
validateFormValues = formProps => {
const { firstName, lastName } = this.props;
const nextFirstName = formProps.firstName;
const nextLastName = formProps.lastName;
if (firstName !== nextFirstName || lastName !== nextLastName) {
this.setState({ modalIsOpen: true, formProps });
}
};
handleSubmit = () => {
this.setState({ modalIsOpen: false }, () => {
this.props.updateUser(this.state.formProps);
this.props.reset("ChangeNameForm");
});
};
render = () => (
<div style={{ padding: "0px 20px" }}>
<ShowModal
{...this.state}
{...this.props}
afterOpenModal={this.afterOpenModal}
openModal={this.openModal}
closeModal={this.closeModal}
handleSubmit={this.handleSubmit}
/>
<ChangeNameForm validateFormValues={this.validateFormValues} />
<CurrentStore />
<Values form="ChangeNameForm" />
</div>
);
}
export default connect(
state => ({
firstName: state.user.firstName,
lastName: state.user.lastName
}),
{ updateUser, reset }
)(ChangeNameHOC);
containers/ChangeNameForm.js
import React from "react";
import { Field, reduxForm } from "redux-form";
import RenderField from "../components/RenderField";
const isRequired = value => (!value ? "Required" : undefined);
const ChangeNameForm = ({
handleSubmit,
reset,
submitting,
validateFormValues
}) => (
<form onSubmit={handleSubmit(validateFormValues)}>
<Field
className="uk-input"
label="First Name"
name="firstName"
component={RenderField}
type="text"
placeholder="First Name"
validate={[isRequired]}
/>
<Field
className="uk-input"
label="Last Name"
name="lastName"
component={RenderField}
type="text"
placeholder="Last Name"
validate={[isRequired]}
/>
<button
className="uk-button uk-button-primary"
type="submit"
disabled={submitting}
style={{ marginBottom: 20 }}
>
Submit
</button>
<div style={{ float: "right" }}>
<button
className="uk-button uk-button-danger"
type="button"
disabled={submitting}
onClick={reset}
style={{ marginBottom: 20 }}
>
Reset
</button>
</div>
</form>
);
export default reduxForm({
form: "ChangeNameForm"
})(ChangeNameForm);
components/ShowModal.js
import React, { PureComponent } from "react";
import Modal from "react-modal";
const customStyles = {
content: {
minWidth: "400px",
top: "50%",
left: "50%",
right: "auto",
bottom: "auto",
marginRight: "-50%",
transform: "translate(-50%, -50%)"
}
};
Modal.setAppElement("#root");
export default class ShowModal extends PureComponent {
showChange = (name, prevName, nextName) => (
<div>
{name}: <strong>{prevName}</strong> to <strong>{nextName}</strong>
</div>
);
render = () => {
const { firstName, lastName } = this.props;
const { firstName: nextFirstName } = this.props.formProps;
const { lastName: nextLastName } = this.props.formProps;
return (
<div>
<Modal
isOpen={this.props.modalIsOpen}
onAfterOpen={this.props.afterOpenModal}
onRequestClose={this.props.closeModal}
style={customStyles}
contentLabel="Are you sure?"
>
<h3>Are you sure you want to update?</h3>
<div style={{ marginBottom: 20 }}>
{firstName !== nextFirstName
? this.showChange("FirstName", firstName, nextFirstName)
: null}
{lastName !== nextLastName
? this.showChange("LastName", lastName, nextLastName)
: null}
</div>
<button
style={{ float: "left" }}
className="uk-button uk-button-primary"
onClick={this.props.handleSubmit}
>
confirm
</button>
<button
style={{ float: "right" }}
className="uk-button uk-button-danger"
onClick={this.props.closeModal}
>
cancel
</button>
</Modal>
</div>
);
};
}

Related

Passing props to modal passes every object

I'm not 100% sure what's going on here. I've got a display component that displays a bunch of cards, using a map based on my database - On the card is an edit button that pops a modal up, passing props over to the edit form.. Here's kinda how it looks:
import React, { useState } from 'react'
import { useQuery, useMutation } from '#apollo/client'
import { GET_ALL_PROJECTS, REMOVE_PROJECT } from '../helpers/queries'
import { makeStyles } from '#material-ui/core/styles'
import DeleteIcon from '#material-ui/icons/Delete'
import EditIcon from '#material-ui/icons/Edit'
import AddForm from './AddForm'
import EditForm from './EditForm'
import AlertMessage from '../Alerts/AlertMessage'
import { Grid, Typography, Card, CardActionArea, CardActions, CardContent, CardMedia, Button, Modal, Backdrop, Fade } from '#material-ui/core'
const useStyles = makeStyles((theme) => ({
modal: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
},
paper: {
backgroundColor: theme.palette.background.paper,
border: '2px solid #000',
boxShadow: theme.shadows[5],
padding: theme.spacing(2, 4, 3),
},
}));
const DisplayProjects = () => {
const styles = useStyles()
const [deleteItem] = useMutation(REMOVE_PROJECT)
const { loading, error, data } = useQuery(GET_ALL_PROJECTS)
const [status, setStatusBase] = useState('')
const [resultMessage, setResultMessage] = useState('')
const [addOpen, setAddOpen] = useState(false)
const [editOpen, setEditOpen] = useState(false)
const onDelete = (id, e) => {
e.preventDefault()
deleteItem({
variables: { id },
refetchQueries: [{ query: GET_ALL_PROJECTS }]
}).then(
res => handleSuccess(res),
err => handleError(err)
)
}
// Handles Result of the Delete Operation
const handleSuccess = (res) => {
console.log(res.data.deleteProject.proj_name)
// console.log('success!');
setResultMessage(res.data.deleteProject.proj_name)
setStatusBase({
msg: `Successfully Deleted ${resultMessage}`,
key: Math.random()
})
}
const handleError = (err) => {
console.log('error')
}
//Handles the Modal for Add Project
const handleAddOpen = () => {
setAddOpen(true);
};
const handleAddClose = () => {
setAddOpen(false);
};
//Handles the Modal for Edit Project
const handleEditOpen = () => {
setEditOpen(true);
};
const handleEditClose = () => {
setEditOpen(false);
};
if (loading) return '...Loading'
if (error) return `Error: ${error.message}`
return (
<div>
<div style={{ marginTop: 20, padding: 30 }}>
<Grid container spacing={8} justify='center' alignItems='center'>
{data.projects.map(p => {
return (
<Grid item key={p._id}>
<Card >
<CardActionArea>
<div style={{ display: 'flex', justifyContent: 'center' }}>
<CardMedia
style={{ width: 400, height: 100, paddingTop: 10, }}
component='img'
alt='Project Image'
height='140'
image={require('../../images/html-css-javascript-lg.jpg')}
/>
</div>
<CardContent >
<Typography gutterBottom variant='h5' component="h2">
{p.proj_name}
</Typography>
<Typography component='p'>
{p.description}
</Typography>
</CardContent>
</CardActionArea>
<CardActions>
<Button>
<DeleteIcon onClick={e => onDelete(p._id, e)} />
</Button>
<Button onClick={handleEditOpen}>
<Modal
open={editOpen}
onClose={handleEditClose}
closeAfterTransition
BackdropComponent={Backdrop}
className={styles.modal}
>
<Fade in={editOpen}>
<div className={styles.paper}>
<EditForm
id={p._id}
close={handleEditClose}
name={p.proj_name}
desc={p.description}
gh={p.gh_link}
live={p.live_link}
img={p.image_url}
/>
</div>
</Fade>
</Modal>
<EditIcon />
</Button>
</CardActions>
</Card>
{ status ? <AlertMessage key={status.key} message={status.msg} /> : null}
</Grid>
)
}
)}
</Grid>
<Button type='button' onClick={handleAddOpen}>Add Project</Button>
<Modal
open={addOpen}
onClose={handleAddClose}
closeAfterTransition
BackdropComponent={Backdrop}
className={styles.modal}
>
<Fade in={addOpen}>
<div className={styles.paper}>
<AddForm close={handleAddClose} />
</div>
</Fade>
</Modal>
</div>
</div >
)
}
export default DisplayProjects
And here's the form. I've destructured out the props into variables and placed them into a state object called details, so they can be overwritten and submitted to the database..
import React, { useState } from 'react'
import { useParams } from 'react-router-dom'
import { useMutation, useQuery } from '#apollo/client'
import { EDIT_PROJECT, GET_ALL_PROJECTS, GET_PROJECT_BY_ID} from '../helpers/queries'
const AddForm = (props) => {
const params = useParams()
const id = params.toString()
// console.log(id);
const [editProjectItem] = useMutation(EDIT_PROJECT)
const {loading, data, error} = useQuery(GET_PROJECT_BY_ID, {
variables: {
id
},
})
const [details, setDetails] = useState({})
if (loading) return '...Loading';
if (error) return <p>ERROR: {error.message}</p>;
if (!data) return <p>Not found</p>;
setDetails(data.projectById)
console.log(data.projectById)
const submitForm = e => {
e.preventDefault()
try {
editProjectItem({
variables: { id, proj_name, description, gh_link, live_link, image_url},
refetchQueries: [{query: GET_ALL_PROJECTS}]
})
}
catch (err) {
console.log('You Goofed')
}
// setDetails({
// proj_name: '',
// description: '',
// gh_link: '',
// live_link: '',
// image_url: ''
// })
props.close()
}
const changeDetails = (e) => {
setDetails({
...details,
[e.target.name]: e.target.value
})
}
const {_id, proj_name, description, gh_link, live_link, image_url} = details
return (
<div key = {_id}>
<h2>Edit {proj_name}</h2>
<form onSubmit = {submitForm} >
<label>
Project Name:
<input
name = 'proj_name'
value = {proj_name}
onChange = {changeDetails}
/>
</label>
<label>Description</label>
<input
name = 'description'
value = {description}
onChange = {changeDetails}
/>
<label>GitHub Link</label>
<input
name = 'gh_link'
value = {gh_link}
onChange = {changeDetails}
/>
<label>Live Link</label>
<input
name = 'live_link'
value = {live_link}
onChange = {changeDetails}
/>
<label>Preview Image</label>
<input
name = 'image_url'
value = {image_url}
onChange = {changeDetails}
/>
<button type = 'submit'>Submit</button>
</form>
</div>
)
}
export default AddForm
The problem I'm running into, is that when I access the modal, the props are sent from literally EVERY Object, instead of the one, and displays the data for the last record instead of the one I want to edit You can see what happens here (I logged props.id in order to test) https://imgur.com/a/pcEKl89
What did I miss? (Disclaimer: I am still a student, and learning the craft.. be gentle on my code please)
EDIT: I just realized that I didn't indicate that this is the final form of the EditForm component. I haven't added the logic in to make the updates yet, I just wanted to get the data showing properly first.
EDIT2: I made some changes to how the ID is passed over, I was already using React-Router, so I went ahead and made a route to /edit/:id and then using useParams(), I got the ID that way. It seems to be working, however now I'm getting a Too many re-renders message. Updated the AddForm code above to reflect the changes..
I figured out the re-render issue.. it was as simple as dropping the setDetails function into a useEffect Hook:
useEffect(()=> {
if(data){
setDetails(data.projectById)
}
},[data])

How to get the raw value of an antd form field using antd-mask-input library?

I am using antd-mask-input library on my Ant Design app to put mask into a field on my antd form. This antd-mask-input library was built using this inputmask-core library.
Here is an working example on codesandbox.io. Fill in any value on both fields and open console. Once you click on Confirm the value of the fields will be logged on console output.
As you can see, the phone const have the value of the field with the mask, but I need it without the mask. There is a getRawValue() method on inputmask-core but I can't figure out how to use it with the MaskedInput component provided by antd-mask-input library.
import React, { Component } from "react";
import { Form, Icon, Input, Button } from "antd";
import { MaskedInput } from "antd-mask-input";
const INPUT_ICON_COLOR = "rgba(0,0,0,.25)";
const FormFields = Object.freeze({
EMAIL: "email",
PHONE: "phone"
});
class Signup extends Component {
handleSubmit = e => {
const { form } = this.props;
const { validateFields } = form;
e.preventDefault();
validateFields((err, values) => {
if (!err) {
const a = form.getFieldValue(FormFields.PHONE);
debugger;
const phone = values[FormFields.PHONE];
const email = values[FormFields.EMAIL];
console.log("phone", phone);
console.log("email", email);
}
});
};
render() {
const { form } = this.props;
const { getFieldDecorator } = form;
return (
<div
style={{
display: "flex",
alignItems: "center",
width: "100%",
flexDirection: "column"
}}
>
<Form onSubmit={this.handleSubmit} className={"login-form"}>
<Form.Item>
{getFieldDecorator(FormFields.PHONE, {
rules: [
{
required: true,
message: "Please type value"
}
]
})(
<MaskedInput
mask="(11) 1 1111-1111"
placeholderChar={" "}
prefix={
<Icon type="phone" style={{ color: INPUT_ICON_COLOR }} />
}
placeholder="Phone"
/>
)}
</Form.Item>
<Form.Item>
{getFieldDecorator(FormFields.EMAIL, {
rules: [{ required: true, message: "Please type value" }]
})(
<Input
type={"email"}
prefix={
<Icon type="mail" style={{ color: INPUT_ICON_COLOR }} />
}
placeholder="Email"
/>
)}
</Form.Item>
<Form.Item>
<Button
type="primary"
htmlType="submit"
className={"login-form-button"}
>
Confirm
</Button>
</Form.Item>
</Form>
</div>
);
}
}
export default Form.create({ name: "signup" })(Signup);
Looking on the source code:
export default class MaskedInput extends Component<MaskedInputProps> {
mask: InputMask;
input!: HTMLInputElement;
....
}
The InputMask reference is under the mask value, therefore you can get the function like so:
this.inputMaskRef.current.mask.getRawValue()
Example:
class Signup extends Component {
inputMaskRef = React.createRef();
handleSubmit = e => {
...
validateFields((err, values) => {
if (!err) {
...
console.log(this.inputMaskRef.current.mask.getRawValue());
}
});
};
render() {
...
return (
<Form ...>
<Form.Item>
{getFieldDecorator(FormFields.PHONE, {...})(
<MaskedInput
ref={this.inputMaskRef}
mask="(11) 1 1111-1111"
...
/>,
)}
</Form.Item>
...
</Form>
);
}
}

Setting the value of DatePicker (from antd) in react-hook-form

I'm trying to figure out how to use the DatePicker from antd with react-hook-form.
Currently, my attempt is:
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import useForm from "react-hook-form";
import { withRouter } from "react-router-dom";
import { useStateMachine } from "little-state-machine";
import updateAction from "./updateAction";
import { Input as InputField, Form, Button, DatePicker, Divider, Layout, Typography, Skeleton, Switch, Card, Icon, Avatar } from 'antd';
import Select from "react-select";
const { Content } = Layout
const { Text, Paragraph } = Typography;
const { Meta } = Card;
const { MonthPicker, RangePicker, WeekPicker } = DatePicker;
const Team = props => {
const { register, handleSubmit, setValue, errors } = useForm();
const [ dueDate, setDate ] = useState(new Date());
const [indexes, setIndexes] = React.useState([]);
const [counter, setCounter] = React.useState(0);
const { action } = useStateMachine(updateAction);
const onSubit = data => {
action(data);
props.history.push("./ProposalBudget");
};
// const handleChange = dueDate => setDate(date);
const handleChange = (e) => {
setValue("dueDate", e.target.value);
}
const onSubmit = data => {
console.log(data);
};
const addMilestone = () => {
setIndexes(prevIndexes => [...prevIndexes, counter]);
setCounter(prevCounter => prevCounter + 1);
};
const removeMilestone = index => () => {
setIndexes(prevIndexes => [...prevIndexes.filter(item => item !== index)]);
};
const clearMilestones = () => {
setIndexes([]);
};
useEffect(() => {
register({ name: dueDate }); // custom register antd input
}, [register]);
Note: i have also tried name: {${fieldName}.dueDate - that doesn't work either.
return (
<div>
<HeaderBranding />
<Content
style={{
background: '#fff',
padding: 24,
margin: "auto",
minHeight: 280,
width: '70%'
}}
>
<form onSubmit={handleSubmit(onSubit)}>
{indexes.map(index => {
const fieldName = `milestones[${index}]`;
return (
<fieldset name={fieldName} key={fieldName}>
<label>
Title:
<input
type="text"
name={`${fieldName}.title`}
ref={register}
/>
</label>
<label>
Description:
<textarea
rows={12}
name={`${fieldName}.description`}
ref={register}
/>
</label>
<label>When do you expect to complete this milestone? <br />
<DatePicker
selected={ dueDate }
// ref={register}
InputField name={`${fieldName}.dueDate`}
onChange={handleChange(index)}
//onChange={ handleChange }
>
<input
type="date"
name={`${fieldName}.dueDate`}
inputRef={register}
/>
</DatePicker>
</label>
<Button type="danger" style={{ marginBottom: '20px', float: 'right'}} onClick={removeMilestone(index)}>
Remove this Milestone
</Button>
</fieldset>
);
})}
<Button type="primary" style={{ marginBottom: '20px'}} onClick={addMilestone}>
Add a Milestone
</Button>
<br />
<Button type="button" style={{ marginBottom: '20px'}} onClick={clearMilestones}>
Clear Milestones
</Button>
<input type="submit" value="next - budget" />
</form>
</Content>
</div>
);
};
export default withRouter(Team);
This generates an error that says: TypeError: Cannot read property 'value' of undefined
setValue is defined in handleChange.
I'm not clear on what steps are outstanding to get this datepicker functioning. Do I need a separate select function?
Has anyone figured out how to plug this datepicker in?
I have also tried:
const handleChange = (e) => {
setValue("dueDate", e.target.Date);
}
and I have tried:
const handleChange = (e) => {
setValue("dueDate", e.target.date);
}
but each of these generations the same error
I have built a wrapper component to work with external controlled component easier:
https://github.com/react-hook-form/react-hook-form-input
import React from 'react';
import useForm from 'react-hook-form';
import { RHFInput } from 'react-hook-form-input';
import Select from 'react-select';
const options = [
{ value: 'chocolate', label: 'Chocolate' },
{ value: 'strawberry', label: 'Strawberry' },
{ value: 'vanilla', label: 'Vanilla' },
];
function App() {
const { handleSubmit, register, setValue, reset } = useForm();
return (
<form onSubmit={handleSubmit(data => console.log(data))}>
<RHFInput
as={<Select options={options} />}
rules={{ required: true }}
name="reactSelect"
register={register}
setValue={setValue}
/>
<button
type="button"
onClick={() => {
reset({
reactSelect: '',
});
}}
>
Reset Form
</button>
<button>submit</button>
</form>
);
}
try this out, let me know if it makes your life easier with AntD.
/* eslint-disable react/prop-types */
import React, { useState } from 'react';
import { DatePicker } from 'antd';
import { Controller } from 'react-hook-form';
import color from '../../assets/theme/color';
import DatePickerContainer from './DatePickerContainer';
function DatePickerAntd(props) {
const { control, rules, required, title, ...childProps } = props;
const { name } = childProps;
const [focus, setFocus] = useState(false);
const style = {
backgroundColor: color.white,
borderColor: color.primary,
borderRadius: 5,
marginBottom: '1vh',
marginTop: '1vh',
};
let styleError;
if (!focus && props.error) {
styleError = { borderColor: color.red };
}
return (
<div>
<Controller
as={
<DatePicker
style={{ ...style, ...styleError }}
size="large"
format="DD-MM-YYYY"
placeholder={props.placeholder || ''}
onBlur={() => {
setFocus(false);
}}
onFocus={() => {
setFocus(true);
}}
name={name}
/>
}
name={name}
control={control}
rules={rules}
onChange={([selected]) => ({ value: selected })}
/>
</div>
);
}
export default DatePickerAntd;
my container parent use react-hooks-form
const { handleSubmit, control, errors, reset, getValues } = useForm({
mode: 'onChange',
validationSchema: schema,
});
<DatePickerAntd
name="deadline"
title={messages.deadline}
error={errors.deadline}
control={control}
required={isFieldRequired(schema, 'deadline')}
/>
like that, its working for me ;-)
Try this:
<DatePicker
selected={ dueDate }
// ref={register}
InputField name={`${fieldName}.dueDate`}
onChange={()=>handleChange(index)}
//onChange={ handleChange }
>
<input
type="date"
name={`${fieldName}.dueDate`}
inputRef={register}
/>
It looks like if you are using onChange={handleChange(index)} it does not pass a function instead you are passing an execution result of that function.
And if you are trying to access event inside handleChange, you should manually pass if from binding scope otherwise, it will be undefined.
onChange={()=>handleChange(index, event)}

Why textbox is not taking any input, I want all formData on form submit action?

I have tried too many times for this below form (React-with-redux), my textbox is not taking any inputs, please suggest me,
Thanks for help, Buddy
import React from 'react';
import './login.css';
import {connect} from 'react-redux'
import { change, Field, reduxForm } from 'redux-form';
import TextField from '#material-ui/core/TextField';
import Button from '#material-ui/core/Button';
import { login } from './Actions';
export const renderTextField = ({
input,
label,
id,
multiLine,
rowsMax,
fullWidth,
disabled,
hintText,
defaultValue,
onChange,
maxLength,
loader,
meta: { touched, error },
customError,
autoFocus,
floatingLabelFixed,
...custom
}) => {
return (
<TextField
id={id}
defaultValue={defaultValue}
autoFocus={autoFocus}
floatingLabelText={label}
floatingLabelFixed={floatingLabelFixed}
errorText={touched && (error || customError)}
multiLine={multiLine}
hintText={hintText}
rowsMax={rowsMax}
disabled={disabled}
fullWidth={true}
className="valuefont"
autoComplete='new-type'
onChange={(event) => onChange}
maxLength={maxLength}
floatingLabelStyle={{ top: '30px', color: '#7a7a7a' }}
floatingLabelFocusStyle={{ color: '#01B9C1' }}
style={{ height: '62px ' }}
inputStyle={{ marginTop: '10px' }}
{...input}
{...custom}
/>
);
};
class Login extends React.Component{
constructor(props){
super(props);
}
handleChange = (e)=>{
// setValues({ ...values, [e.target.name]: e.target.value });
}
onSubmit = (formProps)=>{
debugger
}
render(){
const { handleSubmit } = this.props;
return(
<form noValidate onSubmit={handleSubmit(this.onSubmit)}>
<div>
<Field
name="firstName"
component={renderTextField}
label="First Name"
/>
</div>
<Button variant="contained" color="primary" type="submit">
Login
</Button>
</form>
)
}
}
const mapStateToProps = (state)=>{
return {
loading : state.loginReducer.loading,
error : state.loginReducer.error
}
}
export default connect(mapStateToProps,{login})(reduxForm({
form: 'LoginForm',
keepDirtyOnReinitialize: true,
enableReinitialize: true,
})(Login))
Use renderTextField instead of Field
import React from 'react';
import './login.css';
import {connect} from 'react-redux'
import { change, Field, reduxForm } from 'redux-form';
import TextField from '#material-ui/core/TextField';
import Button from '#material-ui/core/Button';
import { login } from './Actions';
export const renderTextField = ({
input,
label,
id,
multiLine,
rowsMax,
fullWidth,
disabled,
hintText,
defaultValue,
onChange,
maxLength,
loader,
meta: { touched, error },
customError,
autoFocus,
floatingLabelFixed,
...custom
}) => {
return (
<TextField
id={id}
defaultValue={defaultValue}
autoFocus={autoFocus}
floatingLabelText={label}
floatingLabelFixed={floatingLabelFixed}
errorText={touched && (error || customError)}
multiLine={multiLine}
hintText={hintText}
rowsMax={rowsMax}
disabled={disabled}
fullWidth={true}
className="valuefont"
autoComplete='new-type'
onChange={(event) => onChange}
maxLength={maxLength}
floatingLabelStyle={{ top: '30px', color: '#7a7a7a' }}
floatingLabelFocusStyle={{ color: '#01B9C1' }}
style={{ height: '62px ' }}
inputStyle={{ marginTop: '10px' }}
{...input}
{...custom}
/>
);
};
class Login extends React.Component{
constructor(props){
super(props);
}
handleChange = (e)=>{
// setValues({ ...values, [e.target.name]: e.target.value });
}
onSubmit = (formProps)=>{
debugger
}
render(){
const { handleSubmit } = this.props;
return(
<form noValidate onSubmit={handleSubmit(this.onSubmit)}>
<div>
<renderTextField
name="firstName"
label="First Name"
onChange={this.handleChange}
/>
</div>
<Button variant="contained" color="primary" type="submit">
Login
</Button>
</form>
)
}
}
const mapStateToProps = (state)=>{
return {
loading : state.loginReducer.loading,
error : state.loginReducer.error
}
}
export default connect(mapStateToProps,{login})(reduxForm({
form: 'LoginForm',
keepDirtyOnReinitialize: true,
enableReinitialize: true,
})(Login))

Uncaught TypeError: _this.props.onSubmit is not a function when using redux-form

I'm working with a React.js frontend, and when I try to login or signup I get the below error when clicking the login or Sign up buttons.
Uncaught TypeError: _this.props.onSubmit is not a function
The stacktrace is pointing to the following file,
/src/containers/Login/index.js
// #flow
import React, { Component } from 'react';
import { Field, reduxForm } from 'redux-form';
import { Link } from 'react-router-dom';
import { css, StyleSheet } from 'aphrodite';
import Input from '../../components/Input';
const styles = StyleSheet.create({
card: {
maxWidth: '500px',
padding: '3rem 4rem',
margin: '2rem auto',
},
});
type Props = {
onSubmit: () => void,
handleSubmit: () => void,
submitting: boolean,
}
class LoginForm extends Component {
props: Props
handleSubmit = (data) => this.props.onSubmit(data);
// handleSubmit: (data) => this.props.onSubmit(data);
render() {
const { handleSubmit, submitting } = this.props;
return (
<form
className={`card ${css(styles.card)}`}
onSubmit={handleSubmit(this.handleSubmit)}
>
<h3 style={{ marginBottom: '2rem', textAlign: 'center' }}>Login to Sling</h3>
<Field name="email" type="text" component={Input} placeholder="Email" />
<Field name="password" type="password" component={Input} placeholder="Password" />
<button
type="submit"
disabled={submitting}
className="btn btn-block btn-primary"
>
{submitting ? 'Logging in...' : 'Login'}
</button>
<hr style={{ margin: '2rem 0' }} />
<Link to="/signup" className="btn btn-block btn-secondary">
Create a new account
</Link>
</form>
);
}
}
const validate = (values) => {
const errors = {};
if (!values.email) {
errors.email = 'Required';
}
if (!values.password) {
errors.password = 'Required';
}
return errors;
};
export default reduxForm({
form: 'login',
validate,
})(LoginForm);
specifically the below line,
handleSubmit = (data) => this.props.onSubmit(data);
Again, any and all help is greatly appreciated.
Finally, the entire project can be found here.
handleSubmit is a prop "injected" into the Component by redux-form, onSubmit is not... you either have to make sure you're supplying it yourself or just handle the form submit within LoginForm#handleSubmit
You can also do it this way: https://github.com/erikras/redux-form/issues/1163
references:
http://redux-form.com/6.0.0-alpha.4/docs/faq/HandleVsOn.md/
In your App/index.js, you did
<RedirectAuthenticated path="/login" component={Login} {...authProps} />
You can see you did not pass a onSubmit function to the Login component
You can fix it by making these changes to App/index.js
// add this function
const LoginComponent = () => {
return (
<Login onSubmit={() => { console.log("hi") }} />
)
}
class App extends Component {
...
render() {
....
<RedirectAuthenticated path="/login" component={LoginComponent} {...authProps} />
In this case, you will see 'hi' printed to the dev console.

Categories

Resources