Material-ui: [TextField]失去了对重新渲染的关注

创建于 2015-06-08  ·  41评论  ·  资料来源: mui-org/material-ui

如果具有焦点的文本字段由setState呈现,则会失去焦点。

question v0.x

最有用的评论

@scotmatson我可以确认,至少在我看来,如果将包装器中的Container = styled.div转换为普通的非样式化组件div ,则此问题已得到解决。

编辑:
我也分享你的挫败感。 如果这两个库完美地协同工作,那就太好了。 这个问题和特殊性问题是我希望很快解决的两件事,但是我感谢那些自愿奉献时间的人们。

编辑2:
我只是发现了导致问题的原因:在render方法中声明了样式组件! 这导致在每次重新渲染时都重新创建组件,而React无法跟踪哪个元素在整个重新渲染边界上都具有焦点。 这也给我带来了其他一些重新渲染问题。

将所有样式化的组件声明移到组件外部可以解决我失去关注的问题。 可能是一个菜鸟的错误,但在这里没有人提及,所以我想我会回来报告。

所有41条评论

+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保持焦点:

4uo63bzhmj

我仍然明白这一点。 @ shaurya947示例未重现该错误,因为TextField没有初始值属性。
复制使用
<TextField ref="textField" value={this.state.valueToBeEdited} onChange = {this._incrementStateCounter}/>

要么

<TextField ref="textField" defaultValue={this.state.valueToBeEdited} onChange = {this._incrementStateCounter}/>

我还看到似乎是这个问题的回归。 我的TextFields不再专注于重新渲染。 我正在设置TextFieldvalue道具,因此它是一个受控组件。

我确实在控制台中注意到一个新警告:“ TextField正在更改要控制的文本类型的不受控制的输入”。 我不记得以前看到过这种情况,不确定在这里是否相关。 一旦我在TextField输入第一个字符,就会出现警告。

通过一点调试,似乎是某种原因导致TextField失去焦点。 内部onBlur处理程序被调用时,其将切换isFocused的内部状态TextField为false。 如果我在onBlur处理程序中记录document.activeElement (以确定哪个组件具有窃取焦点),则它将记录文档的根目录(正文)。

通过确定模糊事件的源是应用程序中其他位置的Menu中的第一个MenuItem ,我能够进一步缩小范围。 将disableAutoFocus属性设置为true解决了我的问题。 希望这对其他人有帮助。

我打开了一个单独的问题来捕获此问题:#4387。

我可以确认这仍然是一个问题...

在非常简单的登录表单中看到它-我将其与redux表单结合使用,当我与表单交互时,它会导致重新渲染(触摸redux表单中的字段),从而失去了焦点。

解决方法是,如果错误状态发生变化,我只会重新呈现实际的表单。

通过将TextField放入设置为selectable=trueTable内,可以轻松重现此问题。 每当您单击文本字段开始对其进行编辑时,该行都将获得焦点,从而更改其背景颜色(我猜该行的某些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禁用自动对焦。

out

对于那些正在苦苦挣扎而只想工作的人来说,这是一种解决方法,尽管它可能在某些情况下可能不适用,并且可能会出现问题(尽管我并不是完全确定自己)是在实例上使用变量,而不是比国家。 因此,不是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”时,什么都没有显示。 虽然,当我删除它时,它似乎起作用。

我在表行中使用文本字段,这是一个问题:(任何进展?

Psid.txt

我也遇到了同样的错误。 但是在使用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)来解决(在这种情况下)。

所以之前的结构是->功能步骤(道具)->映射类。 所以现在,我删除了函数(中间人),仅删除了Const Steps-> Mapping Class。 我只能推测这为什么起作用(也许某些道具被忽略了),但是它解决了我的情况。

@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,然后将其余的道具散布到其上,因此我不必指定classNamevariant在整个表单上多次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>
  )
}
此页面是否有帮助?
0 / 5 - 0 等级

相关问题

finaiized picture finaiized  ·  3评论

ericraffin picture ericraffin  ·  3评论

chris-hinds picture chris-hinds  ·  3评论

revskill10 picture revskill10  ·  3评论

rbozan picture rbozan  ·  3评论