Reputation: 191
I am having a problem using inputs... I've got two inputs: one has autofocus and the other doesn't. However, when I type in the second input, it loses focus andthe focus returns to the first input.
I've read that React rerenders my component when I type something. I tried putting a key prop and etc, but nothing worked.
In my form (a component called Signup), I have the following:
import React from 'react'
import Input from '../../components/Input'
import styles from './styles.scss'
class Signup extends React.Component {
constructor (props) {
super(props)
this.state = {
name: '',
email: '',
}
}
onSignup (e, userData) {
e.preventDefault()
this.props.onSignup(userData)
}
render () {
return (
<main className={styles.wrapper}>
<div className={styles.formSide}>
<h1>SIGNUP</h1>
<Input
id="name"
label="Name"
onChange={e => this.setState({ name: e.target.value })}
autofocus={true}
/>
<Input
id="email"
label="E-mail"
onChange={e => this.setState({ email: e.target.value })}
/>
</div>
</main>
)
}
}
Signup.propTypes = {
onSignup: React.PropTypes.func.isRequired
}
export default Signup
My component Input has this code:
import React, { PropTypes } from 'react'
import MaskedInput from 'react-maskedinput'
import styles from './styles.scss'
function Input (props) {
let iconComp
if (props.icon) {
iconComp = (<img src={props.icon} alt="Icon" />)
}
let input = ''
if (props.type === 'date') {
input = (
<MaskedInput
ref={inp => inp && props.autofocus && inp.focus()}
onChange={props.onChange}
mask="11/11/1111"
placeholder={props.placeholder}
className={styles.input}
/>
)
} else {
input = (
<input
ref={inp => inp && props.autofocus && inp.focus()}
onChange={props.onChange}
id={props.id}
placeholder={props.placeholder}
type={props.type}
className={styles.input}
/>
)
}
return (
<div className={styles.wrapper}>
<label htmlFor={props.id} className={styles.label}>{props.label}</label>
<br />
{input}
{props.error &&
<span className={styles.error}>
{props.errorMessage}
</span>
}
{iconComp}
</div>
)
}
Input.propTypes = {
id: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
icon: PropTypes.string,
placeholder: PropTypes.string,
type: PropTypes.string,
autofocus: PropTypes.bool,
onChange: PropTypes.func.isRequired,
error: PropTypes.bool,
errorMessage: PropTypes.string
}
Input.defaultProps = {
icon: '',
placeholder: '',
type: 'text',
autofocus: false,
error: false,
errorMessage: ''
}
export default Input
How can I solve this problem?
Upvotes: 2
Views: 3780
Reputation: 8065
The problem is each time you render Input, new line arrow function gets created for ref
and called. So it execute inp.focus()
each time. One way to avoid this is using class component and define ref
callback method as a class function.
class Input extends React.Component {
refCallback(inp){
if(this.props.autofocus) inp.focus();
}
render(){
let input = ''
if (this.props.type === 'date') {
input = (
<MaskedInput
ref={this.refCallback}
onChange={this.props.onChange}
mask="11/11/1111"
placeholder={this.props.placeholder}
/>
)
} else {
input = (
<input
ref={this.refCallback}
onChange={this.props.onChange}
id={this.props.id}
placeholder={this.props.placeholder}
type={this.props.type}
/>
)
}
return (
<div>
<label htmlFor={this.props.id}>{this.props.label}</label>
<br />
{input}
</div>
)
}
}
export default Input
Updated codepen: http://codepen.io/anon/pen/jmqxWy
(My previous code had some issues since I couldn't test it. But now I have updated the code and it works)
Upvotes: 0
Reputation: 1572
so a simple solution is to enhance your SignUp component to have another property called nameAutoFocus and initialize it to true. Use this property to set the autofocus boolean value. Then add the method componentDidMount and inside set nameAutoFocus to false.
class Signup extends React.Component {
constructor (props) {
super(props)
this.state = {
name: '',
email: '',
}
this.nameAutoFocus = true; //new
}
onSignup (e, userData) {
e.preventDefault()
this.props.onSignup(userData)
}
//new
componentDidMount() {
this.nameAutoFocus = false;
}
render () {
return (
<main>
<div>
<h1>SIGNUP</h1>
<Input
id="name"
label="Name"
onChange={e => this.setState({ name: e.target.value })}
autofocus={this.nameAutoFocus}
/>
<Input
id="email"
label="E-mail"
onChange={e => this.setState({ email: e.target.value })}
/>
</div>
</main>
)
}
}
This works because the initial value of nameAutoFocus is passed to the input giving it focus then componentDidMount will run setting it to false so the next time state changes it won't set the autofocus property to true. This essentially is giving it focus only once when initially rendered.
codepen: http://codepen.io/floor_/pen/PmNRKV?editors=0011 don't forget to click run.
Upvotes: 2