In React ES6, why does the input field lose focus after entering a character?

In my component below, the input field loses focus after entering a character. When using the Chrome Inspector, it looks like the entire form is being re-displayed, not just the value attribute of the input field as you type.

I get no errors either from eslint or from Chrome Inspector.

Submitting the form itself works the same as the actual input field when it is located either in the rendering return or when importing as a separate component, but not in the way I encoded it below.

Why is this so?

Home Page Component

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

Text input component

 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; 

Submit Button Component

 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; 
+16
source share
7 answers

What happens is:

When the onChange event occurs, the callback function calls setState with a new title value, which is passed to your text field as a property. At this point, React renders it again, so you lose focus.

My first suggestion would be to provide the keys for your components, in particular the form and input itself. Keys allow React to maintain component identity through visualization.

Edit:

See this key documentation: https://reactjs.org/docs/lists-and-keys.html#keys.

+11
source

this is because you render the form in a function inside render ().

Each time your state / prop changes, the function returns a new form. it made you lose focus.

Try putting what is inside the function in your rendering directly.

  <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> 
+2
source

My problem was that it was redrawn in the component without saving state in the same file. Therefore, as soon as I got rid of the unnecessary component without saving the state and simply inserted the code directly, I had no unnecessary repeated rendering.

 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> 
+1
source

I am new to React and have run into this problem.

Here is what I did to solve:

  1. First move all your components to the components folder, and then import them to where you want to use them.
  2. Make sure all form elements have a name and id property
  3. Make sure all components get a unique key as they walk through the tree

Someone smarter than me might tell us why we can skip the first step and embed everything, so to speak, but that only helped me organize the code.

I think the real problem is that React overrides everything (as already mentioned), and sometimes redrawing occurs in the parent component, which does not have key but needs it.

My problem was that ExpansionPanel components wrapped my custom components for form input. The panels need a key too!

Hope this helps someone else, it drives me crazy!

+1
source

Your form is overwritten when you enter a character, because you have an onChange method that changes state. Each state change causes the form to restart, and therefore the input method loses focus.

Since you are using redux, the best way would be to store the message header value in the redux object. In addition, you may want to use redux-form for your form.

To get the input value without re-rendering , you need to use refs .

0
source

I am not authorized to comment, then this should be the answer. I had a similar problem and the answer from Alex Ian was decisive.

Namely, I had this function

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

it works fine with FF, not Chrome, when displayed as <DisplaySearchArea/> When displayed as {...}, it works with both. The code doesn’t look so “beautiful”, but it works, they have already told me that it tends to abuse lambdas.

0
source

Thanks, Alex. So I solved my problem:

 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... } 
0
source

Source: https://habr.com/ru/post/1264970/


All Articles