В React ES6, почему поле ввода теряет фокус после ввода символа?

В моем компоненте ниже поле ввода теряет фокус после ввода символа. При использовании Chrome Inspector похоже, что вся форма повторно отображается, а не только атрибут value поля ввода при вводе.

Я не получаю никаких ошибок ни от eslint, ни от Chrome Inspector.

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

Почему это так?

Компонент главной страницы

import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as actionPost from '../redux/action/actionPost';
import InputText from './form/InputText';
import InputSubmit from './form/InputSubmit';

class _PostSingle extends Component {
    constructor(props, context) {
        super(props, context);
        this.state = {
            post: {
                title: '',
            },
        };
        this.onChange = this.onChange.bind(this);
        this.onSubmit = this.onSubmit.bind(this);
    }
    onChange(event) {
        this.setState({
            post: {
                title: event.target.value,
            },
        });
    }
    onSubmit(event) {
        event.preventDefault();
        this.props.actions.postCreate(this.state.post);
        this.setState({
            post: {
                title: '',
            },
        });
    }
    render() {
        const onChange = this.onChange;
        const onSubmit = this.onSubmit;
        const valueTitle = this.state.post.title;
        const FormPostSingle = () => (
            <form onSubmit={onSubmit}>
                <InputText name="title" label="Title" placeholder="Enter a title" onChange={onChange} value={valueTitle} />
                <InputSubmit name="Save" />
            </form>
        );
        return (
            <main id="main" role="main">
                <div className="container-fluid">
                    <FormPostSingle />
                </div>
            </main>
        );
    }
}

_PostSingle.propTypes = {
    actions: PropTypes.objectOf(PropTypes.func).isRequired,
};

function mapStateToProps(state) {
    return {
        posts: state.posts,
    };
}

function mapDispatchToProps(dispatch) {
    return {
        actions: bindActionCreators(actionPost, dispatch),
    };
}

export default connect(mapStateToProps, mapDispatchToProps)(_PostSingle);

Компонент ввода текста

import React, { PropTypes } from 'react';

const InputText = ({ name, label, placeholder, onChange, value, error }) => {
    const fieldClass = 'form-control input-lg';
    let wrapperClass = 'form-group';
    if (error && error.length > 0) {
        wrapperClass += ' has-error';
    }
    return (
        <div className={wrapperClass}>
            <label htmlFor={name} className="sr-only">{label}</label>
            <input type="text" id={name} name={name} placeholder={placeholder} onChange={onChange} value={value} className={fieldClass} />
            {error &&
                <div className="alert alert-danger">{error}</div>
            }
        </div>
    );
};

InputText.propTypes = {
    name: PropTypes.string.isRequired,
    label: PropTypes.string.isRequired,
    placeholder: PropTypes.string.isRequired,
    onChange: PropTypes.func.isRequired,
    value: PropTypes.string,
    error: PropTypes.string,
};

InputText.defaultProps = {
    value: null,
    error: null,
};

export default InputText;

Отправить компонент кнопки

import React, { PropTypes } from 'react';

const InputSubmit = ({ name }) => {
    const fieldClass = 'btn btn-primary btn-lg';
    return (
        <input type="submit" value={name} className={fieldClass} />
    );
};

InputSubmit.propTypes = {
    name: PropTypes.string,
};

InputSubmit.defaultProps = {
    name: 'Submit',
};

export default InputSubmit;

Ответ 1

Что происходит, это:

Когда происходит событие onChange, функция обратного вызова вызывает setState с новым значением title, которое передается в ваше текстовое поле в качестве реквизита. В этот момент React рендерит его заново, поэтому вы теряете фокус.

Моим первым предложением было бы предоставить ключи ваших компонентов, в частности форму и сам ввод. Ключи позволяют React сохранять идентичность компонентов посредством визуализации.

Редактировать:

См. Эту документацию по ключам: https://reactjs.org/docs/lists-and-keys.html#keys.

Ответ 2

это потому, что вы визуализируете форму в функции внутри render().

Каждый раз, когда изменяется ваше состояние /prop, функция возвращает новую форму. это заставило вас потерять фокус.

Попробуйте помещать то, что внутри функции в ваш рендеринг напрямую.

       <main id="main" role="main">
            <div className="container-fluid">
                <FormPostSingle />
            </div>
        </main>

==== >

       <main id="main" role="main">
            <div className="container-fluid">
                <form onSubmit={onSubmit}>
                    <InputText name="title" label="Title" placeholder="Enter a title" onChange={onChange} value={valueTitle} />
                    <InputSubmit name="Save" />
                </form>
            </div>
        </main>

Ответ 3

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

render(){
   const NewSocialPost = () => 
       <div className='new-post'>
           <input
                onChange={(e) => this.setState({ newSocialPost: e.target.value })}
                value={this.state.newSocialPost}/>
           <button onClick={() => this._handleNewSocialPost()}>Submit</button>
      </div>

return (
            <div id='social-post-page'>
                <div className='post-column'>
                    <div className='posts'>
                        <Stuff />
                    </div>
                    <NewSocialPost />
                </div>
                <MoreStuff />
            </div>

Ответ 4

Я новичок в React, и столкнулся с этой проблемой.

Вот что я сделал, чтобы решить:

  1. Сначала переместите все свои компоненты в папку компонентов, а затем импортируйте их туда, где вы хотите их использовать.
  2. Убедитесь, что все элементы формы имеют свойство name и id
  3. Убедитесь, что все компоненты по мере прохождения дерева получают уникальный key

Кто-то умнее меня, возможно, скажет нам, почему мы можем пропустить первый шаг и, так сказать, все встроить, но это только помогло мне организовать код.

Я думаю, что реальная проблема заключается в том, что React переопределяет все (как уже было сказано), и иногда перерисовка происходит в родительском компоненте, который не имеет key но нуждается в нем.

Моя проблема была в том, что компоненты ExpansionPanel обернули мои пользовательские компоненты для ввода формы. Панели нужен key также!

Надеюсь, это поможет кому-то еще, это сводило меня с ума!

Ответ 5

Ваша форма перезаписывается при вводе символа, потому что у вас есть метод onChange, который изменяет состояние. Каждое изменение состояния заставляет форму перезапускать, и поэтому метод ввода теряет фокус.

Поскольку вы используете redux, лучшим способом было бы сохранить значение заголовка сообщения в объекте redux. Кроме того, вы можете захотеть использовать redux-form для своей формы.

Чтобы получить значение ввода без re-rendering, вам нужно использовать refs.

Ответ 6

Я не уполномочен комментировать, тогда это должен быть ответ. У меня была похожая проблема, и ответ от Алекса Яна был решительным.

А именно у меня была эта функция

const DisplaySearchArea =()=>{return (arrayOfSearchFieldNames.map((element, index)=>{return(<div key ={index} className = {inputFieldStyle}><input  placeholder= {arrayOfPlaceholders[index]} type="text" className='border-0'
value={this.state[element]}
onChange={e => {this.setState({ [element]: e.target.value }); console.log(e.target)}}
onMouseEnter={e=>e.target.focus()}/></div>)}))}

он работает нормально с FF, а не с Chrome, когда отображается как <DisplaySearchArea/> Когда отображается как {...}, он работает с обоими. Код выглядит не так "красиво", но работает, мне уже сказали, что он склонен злоупотреблять лямбдами.

Ответ 7

Спасибо, Алекс. Таким образом, я решил свою проблему:

constructor(props, context) {
    ...
    this.FormPostSingle = this.FormPostSingle.bind(this);
}
FormPostSingle() {
        const onChange = this.onChange;
        const onSubmit = this.onSubmit;
        const valueTitle = this.state.post.title;
        return (
        <form onSubmit={onSubmit}>
                <InputText name="title" label="Title" placeholder="Enter a title" onChange={onChange} value={valueTitle} />
                <InputSubmit name="Save" />
            </form>        );
}
render() {
    let FormPostSingle = this.FormPostSingle
    return...
}