In React ES6, why does the input field lose focus

2020-02-10 01:36发布

In my component below, the input field loses focus after typing a character. While using Chrome's Inspector, it looks like the whole form is being re-rendered instead of just the value attribute of the input field when typing.

I get no errors from either eslint nor Chrome Inspector.

Submitting the form itself works as does the actual input field when it is located either in the render's return or while being imported as a separate component but not in how I have it coded below.

Why is this so?

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

8条回答
别忘想泡老子
2楼-- · 2020-02-10 01:37

What's happening is this:

When your onChange event fires, the callback calls setState with the new title value, which gets passed to your text field as a prop. At that point, React renders it anew, which is why you lose focus.

My first suggestion would be to provide your components keys, particularly the form and the input itself. Keys allow React to retain the identity of components through renders.

Edit:

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

查看更多
放荡不羁爱自由
3楼-- · 2020-02-10 01:39

I'm new to React, and have been running into this issue.

Here's what I did to solve:

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

Someone smarter than me can probably tell us why we can skip step one and keep everything inline so to speak, but this just helped me organize the code.

I think the real issue is React is rerendering everything (as already stated) and sometimes that rerender is happening on a parent component that doesn't have a key but needs one.

My problem was with ExpansionPanel components wrapping my custom components for form inputs. The panels needed key as well!

Hope this helps someone else out there, this was driving me crazy!

查看更多
Ridiculous、
4楼-- · 2020-02-10 01:48

My issue was it was rerendering in a stateless component in the same file. So once I got rid of that unecessary stateless component and just put the code in directly, I didn't have unecessary rerenders

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>
查看更多
聊天终结者
5楼-- · 2020-02-10 01:51

Thanks, Alex. This way I solved my issue:

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...
}
查看更多
甜甜的少女心
6楼-- · 2020-02-10 01:56

it is because you are rendering the form in a function inside render().

Every time your state/prop change, the function returns a new form. it caused you to lose focus.

Try putting what's inside the function into your render 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>
查看更多
我只想做你的唯一
7楼-- · 2020-02-10 01:58

I am not authorised to comment then it must be an answer. I had similar issue and Answer from Alex Yan was corect.

Namely I had that 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>)}))}

that behaves OK with FF and not with Chrome when rendered as <DisplaySearchArea /> When render as {...} it's OK with both. That is not so 'beaty' looking code but working, I have already been told to have tendency to overuse lambdas.

查看更多
登录 后发表回答