Используя редукционную форму, я теряю фокус после ввода первого символа

Я использую redux-form и проверку размытия. После того, как я ввожу первый символ в элемент ввода, он теряет фокус, и мне приходится снова щелкать по нему, чтобы продолжить ввод. Это происходит только с первым символом. Последующие типы символов остаются в фокусе. Вот мой базовый пример формы входа:

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

person Gregg    schedule 03.10.2016    source источник


Ответы (8)


Это происходит из-за того, что вы переопределяете renderField как новый компонент каждый раз при рендеринге, что означает, что он выглядит как новый компонент для React, поэтому он размонтирует исходный и повторно смонтирует новый. .

Вам нужно будет поднять его:

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

...
person riscarrott    schedule 03.10.2016
comment
Дох! Спасибо. Я попробую. - person Gregg; 03.10.2016
comment
Спасибо, это сработало для меня, даже с функциональной составляющей. - person Julian; 18.11.2019

Как упоминал @riscarrott, поместите renderField вне класса компонента.

Но я все еще теряю фокус. И после тестирования я пришел к выводу, что повторная визуализация выполняется из-за использования каррированной функции (возврат другой функции, а не возврата элемента . напрямую).

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

Затем, если ваша renderField является каррированной функцией:

тогда не делайте ????????????????:

     //.....

     <Field name="email" type="text" component={renderField('input')} label="Email Address" />
     <Field name="desc" component={renderField('textarea')} label="Email Address" />

Но сделайте следующее ???????????????? :

  // 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" />
person Abdennour TOUMI    schedule 12.08.2017
comment
это хорошо объясненный ответ, который, возможно, сэкономил мне часы. спасибо @abdennour ! - person rakitin; 31.12.2018
comment
Добро пожаловать @rakitin - person Abdennour TOUMI; 14.04.2020

Что сработало для меня, так это рефакторинг компонента на основе arrowFunction в компонент на основе класса, поскольку поведение компонентов InputForm было странным. Каждый раз, когда значение каждого ввода изменялось, все они перерисовывались даже после разделения каждого inputType на отдельные компоненты. Больше ничего не оставалось, как изменить основной компонент на классовый. Я предполагаю, что это может быть вызвано самой redux-form.

person Advem    schedule 19.10.2019

Это также может произойти, если вы определили стилизованные компоненты внутри функции рендеринга. Вы должны определить их вне своего класса. Нравится:

const Row = styled.div`
    justify-content:center;
`;
const Card = styled.div`
    width:18rem;
    padding:1rem;
 `;
 class Login extends Component{
person subharb    schedule 14.01.2020

У меня точно такая же проблема. я решил свой, изменив компонент на компонент класса, и я удалил всю конфигурацию стиля css из render().

person Amani Kanu    schedule 29.06.2020

У меня такая же проблема. Я решил это, когда добавил свою форму реакции на редукцию в хранилище в 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;
}

person so5tmaker    schedule 22.09.2018

У меня была такая же проблема, и ни один из ответов не помог мне. Но благодаря ответу Advem я понял, что может быть не так: Моя форма требовала пользовательского интерфейса аккордеона, и для этого у меня была переменная состояния в ней:

const ConveyorNotificationSettingsForm = (props) => {
    const {handleSubmit, formValues, dirty, reset, submitting} = props;
    const [expandedSection, setExpandedSection] = useState(null);
...

только с одним расширенным разделом, индекс которого равен expandedSection .

После того, как я выделил аккордеон в отдельный функциональный компонент и переместил туда useState, проблема исчезла.

person Mikhail Batcer    schedule 18.12.2020

на самом деле, это проблема функционального компонента. Я использовал компонент на основе классов с избыточной формой, и моя проблема была решена. Я не знаю точную причину, но редукционная форма перерисовывается, когда мы вводим первое слово, и потери фокусируются. используйте компоненты на основе классов всякий раз, когда вы хотите использовать избыточную форму.

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);
person Community    schedule 14.04.2021