I've been looking at many ways to do this and have even made a HOC and changed it to a class component. I've tried the few ways I have seen such as invoking handleSubmit with the prop. I am using redux tools and I am used to seeing something such as submit_failed or submit_success, but I don't see that and I don't know why the onSubmit function doesn't fire. Here is a sample of one of the ways I did it where I follow the simple example in redux-form docs:
import React from 'react';
import { Field, reduxForm } from 'redux-form';
import { Form, Button, Grid } from 'semantic-ui-react';
import { Link } from 'react-router-dom';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import {signup} from '../../actions/user-actions';
import { required, minValue7, email, renderField } from '../helpers/validations';
let SignupForm = (props) => {
const { handleSubmit } = props.signup;
return (
<Grid centered columns={2}>
<Grid.Column className="home">
<Form className="forms" onSubmit={ handleSubmit }>
<Form.Field inline>
<Field
name="username"
component={renderField}
type="text"
placeholder="Username"
label="Username"
validate={[required]}
/>
</Form.Field>
<Form.Field inline>
<Field
name="email"
component={renderField}
type="email"
placeholder="Email"
label="Email"
validate={[required, email]}
/>
</Form.Field>
<Form.Field inline>
<Field
name="password"
component={renderField}
type="password"
placeholder="Password"
label="Password"
validate={[required, minValue7]}
/>
</Form.Field>
<Link to={'/signup2'}>
<Button type="submit">Save</Button>
</Link>
</Form>
</Grid.Column>
</Grid>
)
}
SignupForm = reduxForm({
form: 'form1'
})(SignupForm)
const mapStateToProps = (state, ownProps) => {
return {
userSignup: state.userSignup
}
}
const mapDispatchToProps = (dispatch) => {
return bindActionCreators({signup}, dispatch)
}
export default connect(mapStateToProps, mapDispatchToProps)(SignupForm)
Also I have tried it where I do const {handleSubmit} = props and then change ```onSubmit={handleSubmit(this.props.signup)}
I ended up having to go with my tactic of using the class syntax with the help of #Mister Epic the redirect was occurring before the action.
import React from 'react';
import { Field, reduxForm } from 'redux-form';
import { Form, Button, Grid } from 'semantic-ui-react';
import { required, minValue7, email, renderField } from '../helpers/validations';
class SignupForm extends React.Component {
constructor(props) {
super(props);
this.submit = this.submit.bind(this);
}
submit(values) {
this.props.signup(values)
this.props.history.push('/form2')
}
render () {
return (
<Grid centered columns={2}>
<Grid.Column className="home">
<Form className="forms" onSubmit={ this.props.handleSubmit(this.props.submit) }>
<Form.Field inline>
<Field
name="username"
component={renderField}
type="text"
placeholder="Username"
label="Username"
validate={[required]}
/>
</Form.Field>
<Form.Field inline>
<Field
name="email"
component={renderField}
type="email"
placeholder="Email"
label="Email"
validate={[required, email]}
/>
</Form.Field>
<Form.Field inline>
<Field
name="password"
component={renderField}
type="password"
placeholder="Password"
label="Password"
validate={[required, minValue7]}
/>
</Form.Field>
<Button type="submit">Save</Button>
</Form>
</Grid.Column>
</Grid>
)
}
}
SignupForm = reduxForm({
form: 'form1'
})(SignupForm)
export default SignupForm
Related
I managed to configure everything as expected with Formik, and the AJAX requests seems fine (it shows me the success message if I enable it), but the Netlify form section is still empty (even if the form is listed and acknowledged by Netlify).
This is my contact component:
(I think the problem is on my ajax code under the onSubmit function)
import React from 'react'
import { Formik, Form, Field, ErrorMessage } from 'formik'
import Layout from "../components/layout"
import SEO from "../components/seo"
const ContactForm = () => (
<Layout>
<SEO title="Form with Formik" />
<main class="page-contact-form">
<h1>Do your booking</h1>
<Formik
initialValues={{ email: '', name: '', start: '', end: '', message: '' }}
validate={values => {
let errors = {};
if (!values.email) {
errors.email = 'Required';
} else if (
!/^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email)
) {
errors.email = 'Invalid email address';
}
return errors;
}}
onSubmit={(values) => {
fetch("/", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: ({
"form-name": "contact",
...values
})
}).then(() => alert("Thank you!"))
}}
>
{({ isSubmitting }) => (
<Form name="contact" data-netlify="true" action="/grazie">
<input type="hidden" name="form-name" value="contact" />
<label>Name and Surname: <br />
<Field type="text" name="name" placeholder="Nome Cognome" />
<ErrorMessage name="name" component="div" />
</label>
<br />
<label>Email: <br />
<Field type="email" name="email" placeholder="Richiesto" />
<ErrorMessage name="email" component="div" />
</label>
<br />
<label>Start and End <br />
<Field type="date" name="start" />
<ErrorMessage name="start" />
<Field type="date" name="end" />
<ErrorMessage name="end" />
</label>
<br />
<label>
Message: <br />
<Field type="text" name="message" />
<ErrorMessage name="message" />
</label>
<br />
<button type="submit" disabled={isSubmitting}>
Submit
</button>
</Form>
)}
</Formik>
</main>
</Layout>
)
export default ContactForm
In the fetch function in onsubmit, it looks like you're sending an object, but the content type is url encoded? Perhaps you need to serialize your object into url encoded format first. There's a host of solutions for that in this question. If you go with the top suggestion:
// https://stackoverflow.com/a/1714899/10340970
const serialize = function(obj) {
var str = [];
for (var p in obj)
if (obj.hasOwnProperty(p)) {
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
}
return str.join("&");
}
...
fetch("/", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: serialize({
"form-name": "contact",
...values
})
I am implementing a component that handles an redux action to add comments but it is not working.No error is generated
I have tried calling the props from other regions in the code but that doesnt seem to work.The addComment Action should add the comments rendered in the DishDetails comments section.However no additions are made.
ActionTypes.js
export const ADD_COMMENT='ADD_COMMENT';
ActionCreators.js
import * as ActionTypes from './ActionTypes';
export const addComment=(dishId,rating, author, comment)=>({
type: ActionTypes.ADD_COMMENT,
payload: {
dishId:dishId,
rating:rating,
author:author,
comment:comment
}
});
comments.js
import { COMMENTS } from '../shared/comments';
import * as ActionTypes from './ActionTypes';
export const Comments= (state= COMMENTS, action) => {
switch(action.type){
case ActionTypes.ADD_COMMENT:
var comment= action.payload;
comment.id= state.length;
comment.date = new Date().toISOString();
return state.concat(comment);
default:
return state;
}
};
MainComponent.js
import React, { Component } from 'react';
import Header from './HeaderComponent';
import Footer from './FooterComponent';
import Menu from './MenuComponent';
import DishDetail from './DishDetail';
import Home from './HomeComponent';
import { Switch, Route, Redirect, withRouter } from 'react-router-dom';
import Contact from './ContactComponent';
import About from './AboutComponent';
import { connect } from 'react-redux';
import {addComment} from '../redux/ActionCreators';
const mapStateToProps = state =>{
return{
dishes: state.dishes,
comments: state.comments,
promotions: state.promotions,
leaders: state.leaders
}
};
const mapDispatchToProps = dispatch => ({
addComment: (dishId,rating, author, comment)=>dispatch(addComment(dishId,rating, author, comment))
});
class Main extends Component {
constructor(props) {
super(props);
}
render() {
const HomePage= ()=>{
return(
<Home dish={this.props.dishes.filter((dish)=>dish.featured)[0]}
promotion={this.props.promotions.filter((promotion)=>promotion.featured)[0]}
leader={this.props.leaders.filter((leader)=>leader.featured)[0]}
/>
);
}
const DishWithId = ({match})=>{
return(
<DishDetail dish={this.props.dishes.filter((dish)=>dish.id === parseInt(match.params.dishId,10))[0]}
comments={this.props.comments.filter((comment)=>comment.dishId=== parseInt(match.params.dishId,10))}
addComment={this.props.addComment}/>
);
}
const AboutPage = ()=>{
return(
<About leaders={this.props.leaders}/>
);
}
return (
<div>
<Header/>
<Switch>
<Route path="/home" component={HomePage} />
<Route exact path="/menu" component={()=><Menu dishes ={this.props.dishes} />} />
<Route path="/menu/:dishId" component={DishWithId}/>
<Route exact path="/aboutus" component={() => <AboutPage leaders={this.props.leaders} />} />}/>
<Route exact path="/contactus" component={Contact}/>
<Redirect to="/home"/>
</Switch>
<Footer/>
</div>
);
}
}
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Main));
DishDetail.js
import React, { Component } from 'react';
import { Card, CardImg, CardImgOverlay, CardText, CardBody, CardTitle, Breadcrumb, BreadcrumbItem , Button, Modal, ModalHeader,ModalBody, Form, FormGroup, Input, Label, Col, Row } from 'reactstrap';
import {Control, LocalForm, Errors} from 'react-redux-form';
import {Link} from 'react-router-dom';
const required = (val) =>val && val.length;
const maxLength = (len) => (val) => !(val) || (val.length <= len);
const minLength = (len) => (val) => val && (val.length >= len);
class DishDetail extends Component{
constructor(props){
super(props);
this.state={
dish:props.dish,
isCommentModalOpen: false,
};
this.toggleCommentModal=this.toggleCommentModal.bind(this);
}
toggleCommentModal(){
this.setState({
isCommentModalOpen:!this.state.isCommentModalOpen
});
}
handleSubmit(props,values){
alert("State" + JSON.stringify(props.addComment(props.dishId, values.rating, values.author, values.comment)));
// this.state.addComment(this.state.dishId, values.rating, values.author, values.comment)
}
render(){
const RenderDish=({dish})=>{
return(
<Card>
<CardImg top src={dish.image} alt={dish.name}/>
<CardBody>
<CardTitle>{dish.name}</CardTitle>
<CardText>{dish.description}</CardText>
</CardBody>
</Card>
);
}
const RenderComments=({comments})=>{
const comment_layout= comments.map((comment)=>{
if(comment.comment!=null){
return(
<div>
{comment.comment}
{comment.author}, {new Intl.DateTimeFormat('en-US',{year:'numeric',month:'short',day:'2-digit'}).format(new Date(Date.parse(comment.date)))}
</div>
);
}else{
return(
<div></div>
);
}
});
return(comment_layout);
}
const CommentForm=()=>{
return(
<Button outline onClick={this.toggleCommentModal}>
<span className="fa fa-edit fa-lg">Submit Comment</span>
</Button>
);
}
if (this.state.dish!==undefined){
return (
<div className="container">
<div className="row">
<Breadcrumb>
<BreadcrumbItem>
<Link to="/menu">Menu</Link>
</BreadcrumbItem>
<BreadcrumbItem active>{this.state.dish.name}
</BreadcrumbItem>
</Breadcrumb>
<div className="col-12">
<h3>{this.state.dish.name}</h3>
<hr/>
</div>
</div>
<div className="row ">
<div className="col-12 col-md-5 m-1">
<RenderDish dish={this.state.dish}/>
</div>
<div className="col-md-5 col-sm-12 m-1">
<h4>Comment</h4>
<RenderComments comments={this.props.comments}
/>
<CommentForm/>
</div>
</div>
<Modal isOpen={this.state.isCommentModalOpen} toggle={this.toggleCommentModal}>
<ModalHeader toggle={this.toggleCommentModal}>
Submit Comment </ModalHeader>
<ModalBody>
<div className="col-12">
<LocalForm onSubmit={(values)=>this.handleSubmit(this.props,values)}>
<Row className="form-group">
<Label htmlFor ="rating" md={2}>Rating</Label>
<Control.select model=".rating" id="rating" name="rating" className="form-control">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
</Control.select>
</Row>
<Row className="form-group">
<Label htmlFor ="name" md={2}>Name</Label>
<Control.text model=".name" className="form-control" id="name" name="name" placeholder="name" validators={{required,minLength: minLength(3),maxLength:maxLength(15)}} />
<Errors className="text-danger"
model=".name"
show="touched"
messages={{
required:'Required',
minLength:'Must be greater than 2 char',
maxLength: 'Must be 15 chars or less'
}}
/>
</Row>
<Row className="form-group">
<Label htmlFor ="feedback" md={2}>Comment</Label>
<Control.textarea model=".message" className="form-control" id="message" name="message" rows="12" />
</Row>
<Row className="form-group">
<Button type="submit" color="primary">
Submit
</Button>
</Row>
</LocalForm>
</div>
</ModalBody>
</Modal>
</div>
);
}else{
return(
<div></div>
);
}
}
}
export default DishDetail;
you are not dispatching to the reducer in the action. Do it like this
export const addComment = (dishId, rating, author, comment) => {
return (dispatch) => {
dispatch({
type: ActionTypes.ADD_COMMENT,
payload: {
dishId: dishId,
rating: rating,
author: author,
comment: comment
}
})
}
};
I'm building a new portfolio site and I want it to have a contact form. The project is built with React and I had planned on using Formspree to add a contact form without a backend. Turns out Formspree no longer allows AJAX calls unless you're a paid subscriber.
Are there any alternatives for contact forms similar to Formspree? I had planned to do something like this but can't due to Formspree's limitations.
I know very little about backend programming and would prefer not to have to dive into Node/Express just for the sake of hooking up a contact form. Is this practical or would just adding a backend be easier at this point?
Hello you can use formcarry for this purpose.
Here is an example code for you:
import React from "react";
import axios from "axios"; // For making client request.
class Form extends React.Component {
constructor(props){
super(props);
this.state = {name: "", surname: "", email: "", message: ""};
}
handleForm = e => {
axios.post(
"https://formcarry.com/s/yourFormId",
this.state,
{headers: {"Accept": "application/json"}}
)
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
e.preventDefault();
}
handleFields = e => this.setState({ [e.target.name]: e.target.value });
render() {
return (
<form onSubmit={this.handleForm}>
<label htmlFor="name">Name</label>
<input type="text" id="name" name="name" onChange={this.handleFields} />
<label htmlFor="surname">Surname</label>
<input type="text" id="surname" name="surname" onChange={this.handleFields} />
<label htmlFor="email">Email</label>
<input type="email" id="email" name="email" onChange={this.handleFields} />
<label htmlFor="message">Your Message</label>
<textarea name="message" id="message" onChange={this.handleFields}></textarea>
<button type="submit">Send</button>
</form>
);
}
}
export default Form;
As of Nov 2019 Formspree supports AJAX forms on the free plan. They recently did a 3 part tutorial series on building forms with React using Formspree for the examples. See: https://formspree.io/blog/react-forms-1/
Here's a snippet of code from part 3 of the above series. It's a simple contact for that performs client-side validation with Formik.
import React, { useState } from "react";
import axios from "axios";
import { Formik, Form, Field, ErrorMessage } from "formik";
import * as Yup from "yup";
const formSchema = Yup.object().shape({
email: Yup.string()
.email("Invalid email")
.required("Required"),
message: Yup.string().required("Required")
});
export default () => {
/* Server State Handling */
const [serverState, setServerState] = useState();
const handleServerResponse = (ok, msg) => {
setServerState({ok, msg});
};
const handleOnSubmit = (values, actions) => {
axios({
method: "POST",
url: "http://formspree.io/YOUR_FORM_ID",
data: values
})
.then(response => {
actions.setSubmitting(false);
actions.resetForm();
handleServerResponse(true, "Thanks!");
})
.catch(error => {
actions.setSubmitting(false);
handleServerResponse(false, error.response.data.error);
});
};
return (
<div>
<h1>Contact Us</h1>
<Formik
initialValues={{ email: "", message: "" }}
onSubmit={handleOnSubmit}
validationSchema={formSchema}
>
{({ isSubmitting }) => (
<Form id="fs-frm" noValidate>
<label htmlFor="email">Email:</label>
<Field id="email" type="email" name="email" />
<ErrorMessage name="email" className="errorMsg" component="p" />
<label htmlFor="message">Message:</label>
<Field id="message" name="message" component="textarea" />
<ErrorMessage name="message" className="errorMsg" component="p" />
<button type="submit" disabled={isSubmitting}>
Submit
</button>
{serverState && (
<p className={!serverState.ok ? "errorMsg" : ""}>
{serverState.msg}
</p>
)}
</Form>
)}
</Formik>
</div>
);
};
As an update in 2021 (not sure when it was introduced or changed) the standard react approach for formspree does not do any redirect and hides any AJAX details from you.
They have more info here - correct at tine of writing but worth checking as it updates quite regularly: https://help.formspree.io/hc/en-us/articles/360053108134-How-To-Build-a-Contact-Form-with-React
import React from 'react';
import { useForm, ValidationError } from '#formspree/react';
function ContactForm() {
const [state, handleSubmit] = useForm("contactForm");
if (state.succeeded) {
return <p>Thanks for joining!</p>;
}
return (
<form onSubmit={handleSubmit}>
<label htmlFor="email">
Email Address
</label>
<input
id="email"
type="email"
name="email"
/>
<ValidationError
prefix="Email"
field="email"
errors={state.errors}
/>
<textarea
id="message"
name="message"
/>
<ValidationError
prefix="Message"
field="message"
errors={state.errors}
/>
<button type="submit" disabled={state.submitting}>
Submit
</button>
</form>
);
}
export default ContactForm;
There are a few extra steps which are contained in the page above but this is the main contact from function
I am trying to integrate react-bootstrap Dropdown component in redux form. This is my code
import React from 'react'
import {Input,Form,FormGroup,FormControl,ControlLabel,Button,ButtonToolbar,Panel,Checkbox,Radio} from 'react-bootstrap'
import { reduxForm,Field,reset } from 'redux-form'
const FieldInput = ({ input, meta, type, placeholder, min, max }) => {
return (
<FormControl
type={type} onChange={input.onChange} />
)
}
class dropDown extends React.Component {
render() {
return (<FormControl componentClass="select" {...this.props} {...this.input} />)
}
}
let Gri = props => {
const { handleSubmit,pristine,submitting} = props
return (
<form onSubmit={handleSubmit}>
<div>
<FormGroup controlId="formInlineName">
<ControlLabel>User Name:</ControlLabel>
<Field name="firstname" component={FieldInput} type="text"/>
</FormGroup>
</div>
<div>
<FormGroup controlId="formControlsSelect">
<ControlLabel>Category:</ControlLabel>
<Field name="category_id" component={dropDown}>
<option value="1">Health</option>
<option value="2">Municipality Roads</option>
<option value="3">Women and Child Development</option>
</Field>
</FormGroup>
</div>
<Button bsStyle="success" type="submit" disabled={submitting}>SUBMIT</Button>
</form>
)
}
const onSubmit = values => {
console.log(values)
}
Gri= reduxForm({
form: 'gri',
onSubmit,
})(Gri)
export default Gri
In the output the dropdown list is shown but the value is not returned when I click submit. Please help me finding the solution
I'm using redux-form and on blur validation. After I type the first character into an input element, it loses focus and I have to click in it again to continue typing. It only does this with the first character. Subsequent characters types remains focuses. Here's my basic sign in form example:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Field, reduxForm } from 'redux-form';
import * as actions from '../actions/authActions';
require('../../styles/signin.scss');
class SignIn extends Component {
handleFormSubmit({ email, password }) {
this.props.signinUser({ email, password }, this.props.location);
}
renderAlert() {
if (this.props.errorMessage) {
return (
<div className="alert alert-danger">
{this.props.errorMessage}
</div>
);
} else if (this.props.location.query.error) {
return (
<div className="alert alert-danger">
Authorization required!
</div>
);
}
}
render() {
const { message, handleSubmit, prestine, reset, submitting } = this.props;
const renderField = ({ input, label, type, meta: { touched, invalid, error } }) => (
<div class={`form-group ${touched && invalid ? 'has-error' : ''}`}>
<label for={label} className="sr-only">{label}</label>
<input {...input} placeholder={label} type={type} className="form-control" />
<div class="text-danger">
{touched ? error: ''}
</div>
</div>
);
return (
<div className="row">
<div className="col-md-4 col-md-offset-4">
<form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))} className="form-signin">
<h2 className="form-signin-heading">
Please sign in
</h2>
{this.renderAlert()}
<Field name="email" type="text" component={renderField} label="Email Address" />
<Field name="password" type="password" component={renderField} label="Password" />
<button action="submit" className="btn btn-lg btn-primary btn-block">Sign In</button>
</form>
</div>
</div>
);
}
}
function validate(values) {
const errors = {};
if (!values.email) {
errors.email = 'Enter a username';
}
if (!values.password) {
errors.password = 'Enter a password'
}
return errors;
}
function mapStateToProps(state) {
return { errorMessage: state.auth.error }
}
SignIn = reduxForm({
form: 'signin',
validate: validate
})(SignIn);
export default connect(mapStateToProps, actions)(SignIn);
This happens because you're re-defining renderField as a new component every time you render which means it looks like a new component to React so it'll unmount the original one and re-mounts the new one.
You'll need to hoist it up:
const renderField = ({ input, label, type, meta: { touched, invalid, error } }) => (
<div class={`form-group ${touched && invalid ? 'has-error' : ''}`}>
<label for={label} className="sr-only">{label}</label>
<input {...input} placeholder={label} type={type} className="form-control" />
<div class="text-danger">
{touched ? error: ''}
</div>
</div>
);
class SignIn extends Component {
...
render() {
const { message, handleSubmit, prestine, reset, submitting } = this.props;
return (
<div className="row">
<div className="col-md-4 col-md-offset-4">
<form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))} className="form-signin">
<h2 className="form-signin-heading">
Please sign in
</h2>
{this.renderAlert()}
<Field name="email" type="text" component={renderField} label="Email Address" />
<Field name="password" type="password" component={renderField} label="Password" />
<button action="submit" className="btn btn-lg btn-primary btn-block">Sign In</button>
</form>
</div>
</div>
);
}
}
...
As #riscarrott mentioned, put renderField outside of component class .
But I am still losing focus .. And after testing, I concluded the re-rendering is done because of using curried function (return another function, and not return element . directly) .
const const renderField = (InputComponent = 'input') => ({ input, label, type, meta: { touched, invalid, error } }) => (
<div class={`form-group ${touched && invalid ? 'has-error' : ''}`}>
<label for={label} className="sr-only">{label}</label>
<InputComponent {...input} placeholder={label} type={type} className="form-control" />
<div class="text-danger">
{touched ? error: ''}
</div>
</div>
);
Then, if your renderField is a curried function :
then , don't do 😔😔😔😔:
//.....
<Field name="email" type="text" component={renderField('input')} label="Email Address" />
<Field name="desc" component={renderField('textarea')} label="Email Address" />
But , do the following 🙂🙂🙂🙂 :
// outside component class
const InputField = renderField('input');
const TextAreaField = renderField('textarea');
// inside component class
<Field name="email" type="text" component={InputField} label="Email Address" />
<Field name="desc" component={TextAreaField} label="Email Address" />
What worked for me was refactoring arrowFunction-based Component to class-based Component as the behavior of InputForm components was weird. Every time the value of each input was changed they all rerendered even after splitting each inputType to separated components. There was nothing else left to fix but changing main component to class-based. I guess it may be caused by redux-form itself.
This can also happen if you have defined styled-components inside your render function.
You should define them outside your class.
Like this:
const Row = styled.div`
justify-content:center;
`;
const Card = styled.div`
width:18rem;
padding:1rem;
`;
class Login extends Component{
i have the same problem. i resolved mine by changing the component to Class component and i removed all the css style config from render().
I had the same problem. I solved it when I added my react redux form to the store in the createForms():
export const ConfigureStore = () => {
const store = createStore(
combineReducers({
tasks: Tasks,
task: Task,
image: Image,
admin: Admin,
pageId: PageID,
fieldValues: FieldValues,
formValues: FormValues,
...createForms({
createTask: initialTask,
editTask: initialEditTask
})
}),
applyMiddleware(thunk, logger)
);
return store;
}
I had the same problem, and none of the answers worked for me.
But thanks to Advem's answer I got an idea of what could be wrong:
My form required accordion UI, and for that I had state variable in it:
const ConveyorNotificationSettingsForm = (props) => {
const {handleSubmit, formValues, dirty, reset, submitting} = props;
const [expandedSection, setExpandedSection] = useState(null);
...
with only one expanded section, that with its index equal to expandedSection .
After I extracted the accordion to a separate functional component and moved useState there, the problem was gone.
actually, this is a problem with the function component. I used a class-based component with redux form and my problem solved. I don't know the exact reason but redux form re-renders when we enter the first word and losses focus. use class-based components whenever you want to use redux form.
class StreamCreate extends React.Component{
rendorInput(formProps){
return <input {...formProps.input} />;
}
render(){
return (
<Container maxWidth="lg">
<form action="">
<Field name="title" component={this.rendorInput}/>
<Field name="description" component={this.rendorInput} />
</form>
</Container>
)
}
}
export default reduxForm({
form: 'createStream'
})( StreamCreate);