Using version 6.0.0-alpha.13
we are encountering behaviour that is hard to debug.
Rendering <Field />
components that have the component={props => ...
syntax we experience the following: On entering text into the input
field, it loses focus after the first key press.
This behaviour does not occur when rendering the component as: <Field component="input" type="text" />
OR after the first character is inserted and any field is refocused
The onChange
event and all other event inherited through context seem to be there when inspect. Test with both versions 6.0.0-alpha.13
and 6.0.0-alpha.14
. Dependancies that may / may not be effecting the issue: react-bootstrap, react 15.1.0.
There seem to be issues with focus changing on various other circumstances, tabs also not jumping to the correct component.
* FACE PALM **
My bad, just traced it through the stack and discovered that it was simply re-rendering the component...
DUH ITS STATLESS
Basically for those who want to know we are cutting our boiler plate by creating a component to wrap error validation, label and other stuff. Also saves on our build size of course :-)
export default class FormField extends Component {
field = props => {
return (
<div>
{props.label && <label htmlFor={props.id}>{props.label}</label>}
{createElement(props.componentToRender, props)}
{props.touched && props.error && <div className="text-danger">{props.error}</div>}
</div>
);
}
render() {
const componentToRender = this.props.component;
return <Field {...this.props} {...componentToRender} component={this.field} />;
}
}
It is not the explanation.
http://redux-form.com/6.0.0-alpha.13/examples/material-ui/
here we see it working without any wrappers.
I have this issue only in [email protected]
in [email protected] everything ok
I have this same issue in alpha.13
.
export default class Register extends Component {
generateInput (field, type) {
return (
<div>
{ field.error &&
<span className={ classes.validationError }>
{ field.error }
</span>
}
<input
type={ type || 'text' }
{...field}
/>
</div>
);
}
render () {
return (
<form
className={ classes.registerForm }
onSubmit={handleSubmit}
>
<div className="form-group">
<label>Username</label>
<Field
name="username"
type="text"
className="form-control"
component={ username => this.generateInput(username) }
/>
</div>
</form>
);
}
}
component={ username => this.generateInput(username) }
You can't do this. This is constructing a new component function on every render.
It must be
component={ this.generateInput }
Exactly I also met this issue in [email protected]
like @beornborn said, in [email protected] everything ok
This issue should be be reopened, @andrewmclagan
@spaceoi It should not. Please read the release note of v6.0.0-alpha.14.
@ooflorent I've read several times but haven't seen any notice about losing focus bug.
By the way, in my another application everything works fine, I implemented both them at the same time, but in one case this bug present, in another not. And I have no idea why so and how to debug this.
The _losing focus_ bug is not a bug. It's the expected React behavior. If you are defining your component as an arrow function on every render
, React is not able to reconcile the DOM and assume it is a new component because the type
property of the previous and the new React element is different. If they are, React will remove the _linked_ DOM tree and create a new one. Which implies that the rendered DOM input
element is not the previous one and cannot restore the focus or the selection.
@beornborn Related discussions: #961, https://github.com/erikras/redux-form/issues/961#issuecomment-219785684 and https://github.com/erikras/redux-form/issues/961#issuecomment-219826459
@ooflorent But why then another changes of this field don't remove focus, and other changes of other fields don't remove focus. And I tried implementation without arrow functions - the same problem. And I didn't change anything, just downgraded the version to 13, and everything works fine!
And I tried implementation without arrow functions - the same problem.
_This_ is an issue. Could you create a minimal sample to reproduce this and paste it here (or link a gist) ?
@ooflorent ok, I'll do it in few hours
@ooflorent
I couldn't reproduce not working without arrow functions example.
But I reproduced situation when it works on 6.0.13 with arrow links.
https://www.youtube.com/watch?v=mWS1ZvmyIZc
I am just changing version and it begins to work.
Wow. I'm having this issue too! When I type the first char, it loses focus, after that it doesn't.
Thanks @ooflorent
I recommend, for any one who met this problem, could read the release note of v6.0.0-alpha.14 with demo in alpha.13 and demo in alpha.15.
Some more clarification for anyone struggling:
component={field =>
arrow function method that's in the examples.render()
functioncomponent={this.generateMyInput}
MyComponent.jsx
file, just before you declare your export class MyComponent extends React.Component {
class.<SomeComponent inputRenderer={::this.renderInput} />
then in SomeComponent <Field component={this.props.inputRenderer}
Those are my observations. I don't know if the last one is expected behaviour, so use at your own risk as maybe it will break in the future?
EDIT: Still unsure how to get this.props
into that function since it's outside the component. If you do component={renderInput.bind(null, this.props)
, you get the losing focus problem again.
any suggestion on this on version 6.0.0-rc.3 having the same issue
EDIT:
@OKNoah I found the solution :)
To pass custom props to your renderInput function outside of the class, add them to the <Field>
component. like: <Field customProp="hello" {...fieldProps}>
In your function
function renderInput(props) {
console.log(props.input.customProp); // here you access the props
}
I encountered the same problem with material-ui and using a form in a dialog.
Trick was to create a Input component and calling it this way:
import React, {Component} from 'react';
import TextField from 'material-ui/TextField';
const TextInput = props =>
<TextField errorText={props.touched && props.error}
{...props.input}
/>;
export default TextInput
<Field name="my_field" component={TextInput} type="text" placeholder="My placeholder" value={this.props.my_value || ''}/>
If anyone still gets this error, please check this issue.
Don't use react-css-modules
on redux-form
components.
The example in the migration guide:
http://redux-form.com/6.1.1/docs/MigrationGuide.md/
Specifically this line:
<input {...field.input} type={field.type}/> // Type specified below in <Field>
is not valid JSX because of the <Field> inside the comment.
I'm using last version 6.5.0
and i have this problem, a also find this;
If I write this:
<Field {...this.props} component={::this.renderField}/>
I have problem with focus,
BUT if
<Field {...this.props} component={this.renderField}/>
I don't have problem with focus
Hi @xander9112, ::this.renderField
is effectively creating a new function everytime it's run.
It's the same as this.renderField.bind(this)
, if I'm not mistaken.
@gustavohenke I am getting the same issue and here is a small example of my implementation. I am getting this only when I wrap the
@cssModules({
...styles,
...config,
})
class TextField extends Component {
render() {
const {
label,
cursor,
input: {
onChange,
onFocus,
onBlur: reduxOnBlur,
value,
},
meta: {
error,
touched,
},
onBlur,
readOnly,
description,
className,
orientation,
fieldArray,
name,
popup,
password,
upload,
placeholder,
page,
} = this.props;
const composedOnBlur = compose(reduxOnBlur, onBlur
? onBlur
: f => f);
const component = (
<span>
<input
type="text"
value={value}
onFocus={onFocus}
readOnly={readOnly}
placeholder={placeholder}
autoFocus={cursor ? true : false}
styleName={page ? 'input' : 'input-popup'}
onBlur={() => composedOnBlur(value, name)}
onChange={event => onChange(event.target.value)}
/>
{fieldArray === true ?
null
:
<div className="error-field">
{touched && error && <span styleName="error">{error}</span>}
</div>
}
</span>
);
const textFieldHorizontal = makeHorizontalComponent(
className,
label,
description,
component,
popup,
upload,
);
return textFieldHorizontal;
}
}
export default TextField;
and this is how I am using the TextField component:
<Popup
title="Voicemail"
resetForm={resetForm}
formName={this.formName}
initialState={initialForm}
currentFormValues={formValues}
initializeForm={initializeForm}
onButtonClick={this.handleVoicemailValues}
>
<Field
component={TextField}
name="voicemail.email"
label="Email Address"
className="row-popup" popup
syncErrors={errors && errors.voicemail ? errors.voicemail.email : ''}
description="The voice message will be emailed in an MP3 format to this address"
/>
</Popup>
just for reference, this is my popup.jsx
@cssModules(styles)
class Popup extends Component {
constructor(props) {
super(props);
this.handleDeleteClick = this.handleDeleteClick.bind(this);
}
componentDidMount() {
const { initializeForm, currentFormValues, formName } = this.props;
initializeForm(formName, currentFormValues);
}
handleCancelClick() {
const { resetForm, formName, closePortal, onPopupCloseClick } = this.props;
typeof onPopupCloseClick === 'function' ? onPopupCloseClick() : null;
resetForm(formName);
closePortal();
}
handleButtonClick() {
const {
closePortal,
onButtonClick,
} = this.props;
typeof onButtonClick === 'function' ? onButtonClick() : null;
closePortal();
}
render() {
const {
title,
subtitle,
fullwidth,
alertType,
alertName,
fullscreen,
deleteText,
deletePopup,
buttonLabel,
popupDelete,
popupSubmit,
searchField,
} = this.props;
const fullpage = (
<div styleName="overlay">
<div styleName="full-container">
{this.props.children}
</div>
</div>
);
const popup = (
<div styleName="overlay">
<div styleName="container">
{title ?
<div styleName="title-container">
<span styleName="title">{title}</span>
{subtitle &&
<span>
<span styleName="dash">–</span>
<span styleName="subtitle">{subtitle}</span>
</span>
}
<span styleName="close" onClick={:: this.handleCancelClick} className="icon-close" />
</div>
:
null
}
<div styleName='content'>
{this.props.children}
</div>
<div styleName="footer-container">
<Button onButtonClick={:: this.handleButtonClick} label={buttonLabel} />
</div>
</div>
</div>
);
return (
<div>
{popup}
</div>
);
}
}
export default Popup;
Here is what i am getting in the redux dev tools:
For me it was .bind(this)
that caused the problem: component={renderInput.bind(this)}
What's the status of this ticket and why it's closed? I still have this issue in v.7.
@ooflorent Do you know how to solve this problem? Fields losing focus after first onChange, so I stopped using class & render()
, then got this problem... I tried to use prop-types
, but it dosen't work.
"react": "^15.6.1",
"redux": "^3.7.2",
"redux-form": "^7.0.3",
./src/view/ViewSignin/FormSignin.js
'use strict';
import React, { Component } from 'react';
import Radium from 'radium';
import { Link } from 'react-router-dom';
import {
Field,
reduxForm
} from 'redux-form';
const renderField = ({
input,
type,
label
}) => (
<div>
<input { ...input } type={ type } placeholder={ label } />
</div>
);
const FormSignin = (props) => {
const { handleSubmit } = props;
return (
<form className='frm-signin' onSubmit={ handleSubmit }>
<div className='border-box xy-center row field-area'>
<div className='fields'>
<Field
name="userAccount"
component={ renderField }
type="text"
label='电子邮件 / 手机号'/>
<i></i>
<Field
name="userPassword"
component={ renderField }
type="password"
label='密码'/>
</div>
</div>
<div className='border-box xy-center row button-area'>
<div>
<button
type="submit">
<span className='xy-center'>
登录
</span>
</button>
</div>
</div>
<div className='border-box row others-area'>
<Field
className='chk-keep-signin'
name="keepSignin"
id="keep-signin"
component="input"
type="checkbox"
/>
<label className='lbl-keep-signin' htmlFor="keep-signin">
保持登录
</label>
<Link className='right btn-signup'
to='signin'>
立即注册
</Link>
<Link className='right clean-link btn-forget-password'
to='signin'>
忘记密码
</Link>
</div>
</form>
);
}
export default reduxForm({
form: 'signin'
})(FormSignin);
If you still have this problem, you're probably trying to send some properties to your component and you are mistakenly doing it by passing them as arguments to the function in front of the component.
like this:
<FieldArray name="members" props={{verb}} component={renderInput.bind(null, someProp} />
//or
<FieldArray name="members" props={{verb}} component={renderInput(someProb)} />
which regenerates the component every time (which seems like the component is just losing focus).
The correct way is to use the props
property of Field
or FieldArray
or whatever redux-form component you are using. Like this:
<FieldArray name="members" props={{someProp}} component={renderMemebrs} />
Then inside renderMembers
, get the property like this:
const renderMemebrs = (props) => {
let {someProp} = props;
...
I am not passing props, not using a function in render and I still have this problem.
try to wrap the class component with the CSSModules inside reduxForm instead of using decorator
This particular component and its parents are not using css modules.
Thanks @i6mi6. It was .bind
for me too.
I had to change from component={this.getComponent.bind(this)}
to binding in constructor.
constructor(props) {
super(props);
this.getComponent = this.getComponent.bind(this);
}
`component={this.getComponent}`
I had the same problem with an html table in which I have input text lines in a column. inside a loop I read a json object and I create rows in particular I have a column with inputtext.
http://reactkungfu.com/2015/09/react-js-loses-input-focus-on-typing/
I managed to solve it in the following way
import { InputTextComponent } from './InputTextComponent';
//import my inputTextComponent
...
var trElementList = (function (list, tableComponent) {
var trList = [],
trElement = undefined,
trElementCreator = trElementCreator,
employeeElement = undefined;
// iterating through employee list and
// creating row for each employee
for (var x = 0; x < list.length; x++) {
employeeElement = list[x];
var trNomeImpatto = React.createElement('tr', null, <td rowSpan="4"><strong>{employeeElement['NomeTipologiaImpatto'].toUpperCase()}</strong></td>);
trList.push(trNomeImpatto);
trList.push(trElementCreator(employeeElement, 0, x));
trList.push(trElementCreator(employeeElement, 1, x));
trList.push(trElementCreator(employeeElement, 2, x));
} // end of for
return trList; // returns row list
function trElementCreator(obj, field, index) {
var tdList = [],
tdElement = undefined;
//my input text
var inputTextarea = <InputTextComponent
idImpatto={obj['TipologiaImpattoId']}//index
value={obj[columns[field].nota]}//initial value of the input I read from my json data source
noteType={columns[field].nota}
impattiComposite={tableComponent.state.impattiComposite}
//updateImpactCompositeNote={tableComponent.updateImpactCompositeNote}
/>
tdElement = React.createElement('td', { style: null }, inputTextarea);
tdList.push(tdElement);
var trComponent = createClass({
render: function () {
return React.createElement('tr', null, tdList);
}
});
return React.createElement(trComponent);
} // end of trElementCreator
});
//my tableComponent
var tableComponent = createClass({
// initial component states will be here
// initialize values
getInitialState: function () {
return {
impattiComposite: [],
serviceId: window.sessionStorage.getItem('serviceId'),
serviceName: window.sessionStorage.getItem('serviceName'),
form_data: [],
successCreation: null,
};
},
//read a json data soure of the web api url
componentDidMount: function () {
this.serverRequest =
$.ajax({
url: Url,
type: 'GET',
contentType: 'application/json',
data: JSON.stringify({ id: this.state.serviceId }),
cache: false,
success: function (response) {
this.setState({ impattiComposite: response.data });
}.bind(this),
error: function (xhr, resp, text) {
// show error to console
console.error('Error', xhr, resp, text)
alert(xhr, resp, text);
}
});
},
render: function () {
...
React.createElement('table', {style:null}, React.createElement('tbody', null,trElementList(this.state.impattiComposite, this),))
...
}
//my input text
var inputTextarea = <InputTextComponent
idImpatto={obj['TipologiaImpattoId']}//index
value={obj[columns[field].nota]}//initial value of the input I read //from my json data source
noteType={columns[field].nota}
impattiComposite={tableComponent.state.impattiComposite}//impattiComposite = my json data source
/>//end my input text
tdElement = React.createElement('td', { style: null }, inputTextarea);
tdList.push(tdElement);//add a component
//./InputTextComponent.js
import React from 'react';
export class InputTextComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
idImpatto: props.idImpatto,
value: props.value,
noteType: props.noteType,
_impattiComposite: props.impattiComposite,
};
this.updateNote = this.updateNote.bind(this);
}
//Update a inpute text with new value insert of the user
updateNote(event) {
this.setState({ value: event.target.value });//update a state of the local componet inputText
var impattiComposite = this.state._impattiComposite;
var index = this.state.idImpatto - 1;
var impatto = impattiComposite[index];
impatto[this.state.noteType] = event.target.value;
this.setState({ _impattiComposite: impattiComposite });//update of the state of the father component (tableComponet)
}
render() {
return (
<input
className="Form-input"
type='text'
value={this.state.value}
onChange={this.updateNote}>
</input>
);
}
})
I just came across the same issue. Reading this blog helped me fixing it, thank you OkNoah!
As mentioned above it is not a bug but a desired effect from react. So if you want your own component then make sure you declare it in another file and import it or declare it in the same file but out of your stateful/stateless component. React recognizes new components/arrays/objects etc. based on their reference/key given thus if it's a new ref. then react will drop the old and use the new whatsoever object!
I had the same problem trying to pass down a prop to my custom react native input like:
<Field
name="email"
component={props => <EmailField dark {...props} />}
/>
Solved this way:
<Field
name="email"
dark
component={EmailField}
/>
I changed the place of dark
prop. Redux-form pass all extra props of Field to the component={EmailField}
* FACE PALM **
My bad, just traced it through the stack and discovered that it was simply re-rendering the component...
DUH ITS STATLESS
Basically for those who want to know we are cutting our boiler plate by creating a component to wrap error validation, label and other stuff. Also saves on our build size of course :-)
export default class FormField extends Component { field = props => { return ( <div> {props.label && <label htmlFor={props.id}>{props.label}</label>} {createElement(props.componentToRender, props)} {props.touched && props.error && <div className="text-danger">{props.error}</div>} </div> ); } render() { const componentToRender = this.props.component; return <Field {...this.props} {...componentToRender} component={this.field} />; } }
It's worked to me too) Rerender - this is the clue.
component={ username => this.generateInput(username) }
You can't do this. This is constructing a new component function on every render.
It must be
component={ this.generateInput }
You have helped someone 4 years after this comment, thank you :)
included the next code in tag input:
ref={(input) => {
if (input) {
input.focus();
}
}}
Before:
<input
defaultValue={email}
className="form-control"
type="email"
id="email"
name="email"
placeholder={"[email protected]"}
maxLength="15"
onChange={(e) => validEmail(e.target.value)}
/>
After:
<input
ref={(input) => {
if (input) {
input.focus();
}
}}
defaultValue={email}
className="form-control"
type="email"
id="email"
name="email"
placeholder={"[email protected]"}
maxLength="15"
onChange={(e) => validEmail(e.target.value)}
/>
Most helpful comment
You can't do this. This is constructing a new component function on every render.
It must be