如果具有焦点的文本字段由setState
呈现,则会失去焦点。
+1
我必须将onBlur事件与defaultValue配合使用,以使Textfields正常工作。
任何人都有变通办法使这项工作?
我认为这不再是一个问题。 参见下面的代码:
//taken from examples/webpack-example
/** In this file, we create a React component which incorporates components provided by material-ui */
const React = require('react');
const RaisedButton = require('material-ui/lib/raised-button');
const TextField = require('material-ui/lib/text-field');
const Dialog = require('material-ui/lib/dialog');
const ThemeManager = require('material-ui/lib/styles/theme-manager');
const LightRawTheme = require('material-ui/lib/styles/raw-themes/light-raw-theme');
const Colors = require('material-ui/lib/styles/colors');
const Main = React.createClass({
childContextTypes: {
muiTheme: React.PropTypes.object,
},
getInitialState () {
return {
muiTheme: ThemeManager.getMuiTheme(LightRawTheme),
counter: 0,
};
},
getChildContext() {
return {
muiTheme: this.state.muiTheme,
};
},
componentWillMount() {
let newMuiTheme = ThemeManager.modifyRawThemePalette(this.state.muiTheme, {
accent1Color: Colors.deepOrange500,
});
this.setState({muiTheme: newMuiTheme});
},
componentDidMount() {
this.refs.textField.focus();
},
render() {
console.log(this.state.counter);
let containerStyle = {
textAlign: 'center',
paddingTop: '200px',
};
let standardActions = [
{ text: 'Okay' },
];
return (
<div style={containerStyle}>
<Dialog
title="Super Secret Password"
actions={standardActions}
ref="superSecretPasswordDialog">
1-2-3-4-5
</Dialog>
<h1>material-ui</h1>
<h2>example project</h2>
<TextField ref="textField" onChange = {this._incrementStateCounter}/>
<RaisedButton label="Super Secret Password" primary={true} onTouchTap={this._handleTouchTap} />
</div>
);
},
_handleTouchTap() {
this.refs.superSecretPasswordDialog.show();
},
_incrementStateCounter () {
this.setState({counter: this.state.counter + 1});
},
});
module.exports = Main;
这是运行中的应用程序(随着TextField输入的更改,更新状态已记录在控制台中)。 您可以看到TextField保持焦点:
我仍然明白这一点。 @ shaurya947示例未重现该错误,因为TextField没有初始值属性。
复制使用<TextField ref="textField" value={this.state.valueToBeEdited} onChange = {this._incrementStateCounter}/>
要么
<TextField ref="textField" defaultValue={this.state.valueToBeEdited} onChange = {this._incrementStateCounter}/>
我还看到似乎是这个问题的回归。 我的TextFields不再专注于重新渲染。 我正在设置TextField
的value
道具,因此它是一个受控组件。
我确实在控制台中注意到一个新警告:“ TextField正在更改要控制的文本类型的不受控制的输入”。 我不记得以前看到过这种情况,不确定在这里是否相关。 一旦我在TextField
输入第一个字符,就会出现警告。
通过一点调试,似乎是某种原因导致TextField
失去焦点。 内部onBlur
处理程序被调用时,其将切换isFocused
的内部状态TextField
为false。 如果我在onBlur
处理程序中记录document.activeElement
(以确定哪个组件具有窃取焦点),则它将记录文档的根目录(正文)。
通过确定模糊事件的源是应用程序中其他位置的Menu
中的第一个MenuItem
,我能够进一步缩小范围。 将disableAutoFocus
属性设置为true
解决了我的问题。 希望这对其他人有帮助。
我打开了一个单独的问题来捕获此问题:#4387。
我可以确认这仍然是一个问题...
在非常简单的登录表单中看到它-我将其与redux表单结合使用,当我与表单交互时,它会导致重新渲染(触摸redux表单中的字段),从而失去了焦点。
解决方法是,如果错误状态发生变化,我只会重新呈现实际的表单。
通过将TextField
放入设置为selectable=true
的Table
内,可以轻松重现此问题。 每当您单击文本字段开始对其进行编辑时,该行都将获得焦点,从而更改其背景颜色(我猜该行的某些props
可能被设置为selected=true
),因此触发重新渲染,因此您将失去焦点...
因此,您基本上不能在selectable
Table
内使用TextField
Table
,不确定是否仍然是一种好的UX习惯,但是:)
我们注意到,在我们的项目中,我可以尝试推动复制器。
有什么解决办法吗? 在表中使用常规输入文本区域时也要注意这一点。
@ dalexander01找到了一个“解决方案”,但是它似乎非常特定于
@deyceg请这样做,它不能伤害😄。 只需结合<Dialog/>
和<TextField/>
。
<Dialog
open={this.props.showDialog}
title={'New ' + this.props.type}
autoScrollBodyContent={true}
actions={actions}
bodyStyle={styles.dialogContent}
>
<SelectField
name='sub_type'
value={this.props.top_card.sub_type}
onChange={(e, k, payload) => this.props.topCardSelectChange('sub_type', payload)}
floatingLabelText='Type'
>
{menuItemsJSX}
</SelectField>
<TextField
name='title'
className='story-title'
value={this.props.top_card.title}
onChange={this.props.topCardChange}
floatingLabelText='Title'
fullWidth={true}
multiLine={true}
style={styles.title}
/>
<TextField />
</Dialog>
我为我解决了这个问题,您需要为该文本字段提供唯一的ID。
但是,重新渲染后ID不应更改。 这样,React可以跟踪哪个元素被聚焦。
PS我在通过myArray.map呈现的数组中有文本字段,在重新渲染时必须提供相同的键。
编辑:我再次测试,只是myArray上的“键”在整个rerenders中是相同的,从而解决了该问题。
文本字段ID更改为shortid.generate()。
params.map(function(p,i){
return(
<div key={i}> <--- this will always be the same for this particular row.
<div className='param-inner'>
<TextField id={shortid.generate()} value = {p.value} onChange={this.updateParam.bind(this,i)}/> <-- id here can be random, but it is necessary to avoid browser warning "we don't have enough information..."
</div>
<div className='param-inner'>{p.unit}</div>
</div>
)
}.bind(this));
任何人都设法找到解决此问题的方法(不使用refs
)? 这似乎是onChange
和将value
属性设置为使输入失去焦点的状态键的组合。
我也有以下问题,尽管您在内部看到SelectField实现正在调用DropdownMenu https://github.com/callemall/material-ui/blob/ccf712c5733508784cd709c18c29059542d6aad1/src/SelectField/SelectField ,但我通过采用SelectField实现解决了该问题
因此,我还需要它添加一个DropDownMenu,这在内部将组件包装在Menu中,我添加了disableAutoFocus以使TextField像下面显示的那样保持焦点。
也许我们可以从SelectField中公开一个DropDownMenu组件,并允许使用props禁用自动对焦。
对于那些正在苦苦挣扎而只想工作的人来说,这是一种解决方法,尽管它可能在某些情况下可能不适用,并且可能会出现问题(尽管我并不是完全确定自己)是在实例上使用变量,而不是比国家。 因此,不是this.state.value
而是this.value
,而onChange
将被更新。 该组件将没有value
道具。 然后在onChange
处理程序上,您将使用e.target.value
。
我知道实例变量和状态变量之间的唯一区别是实例var不会触发重新渲染。 尽管可能还有其他原因,所以我希望其他人对此有所懈怠。
仍然出现此错误。 有什么解决方法吗?
我发现的解决方法是将shouldComponentUpdate函数覆盖到我的组件,然后在更改文本字段使用的状态值时返回false。
`shouldComponentUpdate(nextProps, nextState) {
if(nextState.email != this.state.email) {
return false;
}
else if(nextState.password != this.state.password) {
return false;
}
else return true;
}
`
这样做可以解决我的问题。 问题似乎是每次状态改变时组件都会渲染,然后重置焦点。 因此,通过在执行onChange事件以设置状态值时使用上述功能来禁用呈现,组件不会重新加载。
@ Aspintyl000这个工作...有点。 当我通过“值”属性设置文本字段的值时,并将该属性设置为“ this.state.value”时,什么都没有显示。 虽然,当我删除它时,它似乎起作用。
我在表行中使用文本字段,这是一个问题:(任何进展?
我也遇到了同样的错误。 但是在使用componentDidMount(){
this.refs.textField.focus();
}
它不会失去焦点。 但是它不显示输入值。
我试图在<Table />
添加<TextField />
<Table />
。 像上述情况一样,我无法集中精力进行输入。
我的解决方案:将onClick
事件添加到selectable=false
。 输入操作后,使用onBlue
事件设置表的selectable=true
。
相当令人讨厌的行为。
在用样式组件包装两个输入框后,我经历了这一点。 外部div完全破坏了行为。 这是该库设计中的一个严重缺陷。
因此,这是一个非常糟糕的解决方法,但以下工作主要是:
``
onSomeValueChanged =事件=> {
this.props.someValueChanged(event.target.value);
const { target } = event;
setTimeout(() => {
target.focus();
}, 10);
};
``
是的,不漂亮...
@scotmatson “相当令人讨厌的行为...该库的设计中存在严重缺陷”。 这是由志愿者制作的免费软件,虽然与styled-components
的关系不错。
@scotmatson我可以确认,至少在我看来,如果将包装器中的Container = styled.div
转换为普通的非样式化组件div
,则此问题已得到解决。
编辑:
我也分享你的挫败感。 如果这两个库完美地协同工作,那就太好了。 这个问题和特殊性问题是我希望很快解决的两件事,但是我感谢那些自愿奉献时间的人们。
编辑2:
我只是发现了导致问题的原因:在render方法中声明了样式组件! 这导致在每次重新渲染时都重新创建组件,而React无法跟踪哪个元素在整个重新渲染边界上都具有焦点。 这也给我带来了其他一些重新渲染问题。
将所有样式化的组件声明移到组件外部可以解决我失去关注的问题。 可能是一个菜鸟的错误,但在这里没有人提及,所以我想我会回来报告。
我似乎遇到的问题是,每当在Object.map()内部渲染TextField(或Input)时,焦点就很多。 地图外部的TextField可以正常工作。 我是否缺少任何道具或其他东西? 我完全迷路了
我尝试了此线程中建议的几乎所有解决方案,但没有任何运气。
粗糙的结构是这个
主文件
...
function Steps(props) {
const { steps } = props;
if(steps) {
return steps.map((step, index) => (
<div key={(step.order === '' ? index : step.order)}>
<NewUxCard step={step} index={index} onChange={props.onChange}
onChangeCard={props.onChangeCard} onParamChange={props.onParamChange}/>
</div>
));
}
}
<div>
<TextField/> //working
<Steps steps={this.props.ux.steps} onChange={this.onChange}
onChangeCard={this.handleChangeCard} onParamChange={this.handleParamChange} />
</div>
新卡
...
<div>
<TextField type="text" fullWidth id={"qq"+this.props.index} label="Name"
value={this.props.step.name} onChange={this.props.onChangeCard('name', this.props.index)} margin="normal" /> //not working - loses focus on each key stroke
</div>
值得注意的是,我正在使用Redux-React,所以状态更改通过input-> action-> reducer进行
有任何想法吗? 谢谢
编辑1:
对问题的进一步研究,我已缩小范围并通过删除用于创建标签的React函数Steps(props)来解决
所以之前的结构是
@piltsen我在v1.5.0上遇到此问题,同时在.map()
渲染TextField-我将其范围缩小到与输入相同的TextField键,因此当它更改时,很可能导致TextField挂载再次失去焦点。 不确定是否对您有帮助-尽管我现在确定已经对它进行了排序
这也是使用渲染道具的症状。 做类似的事情
<HigherOrderComponent
render={hocProps => <TextField value={...} onChange={...} />
/>
可能会导致这种行为。 要修复,请通过设计允许的片段:
<HigherOrderComponentThatDoesntPassOwnState
component={<TextField {...this.props}/>}
/>
在我看来,焦点没有丢失,但是光标从TextField中消失了。 我可以检查document.activeElement等于我的TextField,可以使用键盘命令和鼠标滚轮更改值(类型编号),但是每种类型缩减程序运行的渲染都会发生并且光标丢失。
然后在渲染顶部,我通过id搜索我的TextField并进行
MyTextField.blur();
MyTextField.focus();
,这无济于事。 但是当我使focus()超时时,它似乎正在工作。
编辑:
好吧,在HOC内它仍然无法正常工作,因此似乎仍然需要将其推高。
@ younes0我也可以确认也可以修复此问题。 就我而言,我有一个辅助的无状态组件,该组件将返回带有几个常用道具集的TextField,然后将其余的道具散布到其上,因此我不必指定className
或variant
在整个表单上多次TextField
进行了修复。
可以重新打开它,还是应该重新发行?
我仍然遇到此问题,我正在使用3.2.0
@OmarDavilaP您可以复制一个新期刊吗?
我为我解决了这个问题,您需要为该文本字段提供唯一的ID。
...
但是,重新渲染后ID不应更改。 这样,React可以跟踪哪个元素被聚焦。
PS我在通过myArray.map呈现的数组中有文本字段,在重新渲染时必须提供相同的键。编辑:我再次测试,只是myArray上的“键”在整个rerenders中是相同的,从而解决了该问题。
文本字段ID更改为shortid.generate()。params.map(function(p,i){ return( <div key={i}> <--- this will always be the same for this particular row. <div className='param-inner'> <TextField id={shortid.generate()} value = {p.value} onChange={this.updateParam.bind(this,i)}/> <-- id here can be random, but it is necessary to avoid browser warning "we don't have enough information..." </div> <div className='param-inner'>{p.unit}</div> </div> ) }.bind(this));
是的,它解决了我的问题,基本上我正在使用:
<NewTextField key={user.id+Math.random()}>
我将其替换为:
<NewTextField key={user.id}>
因此,是的,似乎不明智的做法是每次重新渲染组件时都会生成随机Key。
对我来说,问题是,我为TextField创建了包装函数,但是我在组件函数中声明了该函数,一旦移出函数,一切都会按预期工作!
我创建了一个发件箱示例https://codesandbox.io/s/material-demo-msbxl?fontsize=14&hidenavigation=1&theme=dark
我在条件渲染中遇到了这个问题。 我在渲染器中有一个javascript对象,该对象返回.js文件中的各种组件。 抱歉,我的描述不正确。 修复后,我记得会编辑这篇文章。
编辑:是的,我已修复它! 我相信我有条件渲染的方式是渲染每个组件,即使它们没有显示。 我将逻辑移到了渲染之外,现在看来工作正常。 这是旧的代码:
编辑:我不能在这件事上造型,所以没关系。
我今天遇到了这个问题,并为此努力了好几个小时。 就我而言,我做错了是将TextField
包裹在一个无状态组件中,以更好地组织我的页面(如果正在加载数据等,则不渲染某些内容),但是我留下了useState
回调控制了父组件中的TextField
。
为了后代,这里有一个小的codeandbox链接,可重现我的愚蠢: https ://codesandbox.io/s/mui-textedit-losefocus-g6xxb
现在的问题是,如何创建包装TextField
自定义组件? 此错误似乎同时产生了HOC和createElement
:
const MyField= ({ name, ...props }) => (
<TextField
fullWidth
variant="outlined"
size="small"
name={name}
{...props}
/>
);
const MyField= ({ name, ...rest }) => {
return React.createElement(TextField, {
fullWidth: true,
variant: "outlined",
size: "small",
name: name,
{...rest}
});
};
没关系,上面的方法很好用-我意识到我在父组件中定义了自定义组件,这导致了问题。 当移到父范围之外(应该是这样)时,它可以正常工作-我更新了以前的沙箱以显示正确的方法: https :
对我来说,问题是,我为TextField创建了包装函数,但是我在组件函数中声明了该函数,一旦移出函数,一切都会按预期工作!
我创建了一个发件箱示例https://codesandbox.io/s/material-demo-msbxl?fontsize=14&hidenavigation=1&theme=dark
这是我的问题。 谢谢!
没关系,上面的方法很好用-我意识到我在父组件中定义了自定义组件,这导致了问题。 当移到父范围之外(应该是这样)时,它可以正常工作-我更新了以前的沙箱以显示正确的方法: https :
谢谢。 这工作了!
对我来说,问题是,我为TextField创建了包装函数,但是我在组件函数中声明了该函数,一旦移出函数,一切都会按预期工作!
我创建了一个发件箱示例https://codesandbox.io/s/material-demo-msbxl?fontsize=14&hidenavigation=1&theme=dark
@ fleischman718感谢您弄清楚这一点! 删除父组件定义内的TextField的包装组件函数解决了我的问题!
我花了几秒钟的时间弄清楚了沙箱中发生了什么,所以在下面进行了总结:
import { TextField } from '@material-ui/core';
const ParentComponent = () => {
// DON'T DO THIS!!!
// fix issue by moving this component definition outside of the ParentComponent
// or assign the TextField to a variable instead
const TextFieldWrapper = () => (
<TextField />
);
return (
<div>
<TextFieldWrapper />
</div>
)
}
最有用的评论
@scotmatson我可以确认,至少在我看来,如果将包装器中的
Container = styled.div
转换为普通的非样式化组件div
,则此问题已得到解决。编辑:
我也分享你的挫败感。 如果这两个库完美地协同工作,那就太好了。 这个问题和特殊性问题是我希望很快解决的两件事,但是我感谢那些自愿奉献时间的人们。
编辑2:
我只是发现了导致问题的原因:在render方法中声明了样式组件! 这导致在每次重新渲染时都重新创建组件,而React无法跟踪哪个元素在整个重新渲染边界上都具有焦点。 这也给我带来了其他一些重新渲染问题。
将所有样式化的组件声明移到组件外部可以解决我失去关注的问题。 可能是一个菜鸟的错误,但在这里没有人提及,所以我想我会回来报告。