Using redux-form I'm losing focus after typing the first character
Asked Answered
N

8

31

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);
Northnortheast answered 3/10, 2016 at 19:31 Comment(0)
C
60

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>
    );
  }
}

...
Chatav answered 3/10, 2016 at 19:48 Comment(2)
Doh! Thanks. I'll give this a go. – Northnortheast
Thank you, that worked for me, even with a functional component. – Einkorn
G
7

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" />
Gelasias answered 12/8, 2017 at 16:53 Comment(2)
this is a well explained answer that saved me possibly hours. thank you @abdennour ! – Quintal
Welcome @Quintal – Gelasias
V
1

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.

Vexed answered 19/10, 2019 at 11:33 Comment(0)
P
1

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{
Psychosurgery answered 14/1, 2020 at 9:39 Comment(0)
A
1

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().

Amide answered 29/6, 2020 at 23:40 Comment(0)
G
0

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;
}
Geotaxis answered 22/9, 2018 at 5:8 Comment(0)
E
0

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.

Experienced answered 18/12, 2020 at 7:24 Comment(0)
E
0

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);
Escritoire answered 14/4, 2021 at 18:39 Comment(0)

© 2022 - 2025 β€” McMap. All rights reserved.