首页 文章

React.js - 在重新渲染时输入失去焦点

提问于
浏览
40

我只是写入文本输入,在 onChange 事件中我调用 setState ,所以React重新呈现我的UI . 问题是文本输入总是失去焦点,所以我需要再次关注每个字母:D .

var EditorContainer = React.createClass({

    componentDidMount: function () {
        $(this.getDOMNode()).slimScroll({height: this.props.height, distance: '4px', size: '8px'});
    },

    componentDidUpdate: function () {
        console.log("zde");
        $(this.getDOMNode()).slimScroll({destroy: true}).slimScroll({height: 'auto', distance: '4px', size: '8px'});
    },

    changeSelectedComponentName: function (e) {
        //this.props.editor.selectedComponent.name = $(e.target).val();
        this.props.editor.forceUpdate();
    },

    render: function () {

        var style = {
            height: this.props.height + 'px'
        };
        return (
            <div className="container" style={style}>
                <div className="row">
                    <div className="col-xs-6">
                    {this.props.selected ? <h3>{this.props.selected.name}</h3> : ''}
                    {this.props.selected ? <input type="text" value={this.props.selected.name} onChange={this.changeSelectedComponentName} /> : ''}
                    </div>
                    <div className="col-xs-6">
                        <ComponentTree editor={this.props.editor} components={this.props.components}/>
                    </div>
                </div>
            </div>
        );
    }

});

12 回答

  • 50

    没有看到你的其余代码,这是一个猜测 . 创建EditorContainer时,请为组件指定唯一键:

    <EditorContainer key="editor1"/>

    当重新渲染发生时,如果看到相同的密钥,这将告诉React不要破坏并重新生成视图,而是重用 . 然后焦点项目应保持焦点 .

  • 2

    如果它是反应路由器中的问题 <Route/> 使用 render prop而不是 component .

    <Route path="/user" render={() => <UserPage/>} />
    

    失去焦点的原因在于 component prop每次使用 React.createElement 而不是仅仅重新渲染更改 .

    详情:https://reacttraining.com/react-router/web/api/Route/component

  • 1

    我一次又一次地回到这里,总是在最后找到解决方案 . 所以,我会在这里记录,因为我知道我会再次忘记这一点!

    input 在我的案例中失去焦点的原因是因为我正在重新渲染 input 状态变化 .

    越野车代码:

    import React from 'react';
    import styled from 'styled-components';
    
    class SuperAwesomeComp extends React.Component {
      state = {
        email: ''
      };
      updateEmail = e => {
        e.preventDefault();
        this.setState({ email: e.target.value });
      };
      render() {
        const Container = styled.div``;
        const Input = styled.input``;
        return (
          <Container>
            <Input
              type="text"
              placeholder="Gimme your email!"
              onChange={this.updateEmail}
              value={this.state.email}
            />
          </Container>
        )
      }
    }
    

    所以,问题是我总是开始在一个地方编码所有内容以快速测试,然后将其全部分解为单独的模块 . 但是,这里的策略适得其反,因为在输入更改时更新状态会触发渲染功能,并且焦点会丢失 .

    修复很简单,从一开始就进行模块化,换句话说,“将 Input 组件移出 render 函数”

    固定代码

    import React from 'react';
    import styled from 'styled-components';
    
    const Container = styled.div``;
    const Input = styled.input``;
    
    class SuperAwesomeComp extends React.Component {
      state = {
        email: ''
      };
      updateEmail = e => {
        e.preventDefault();
        this.setState({ email: e.target.value });
      };
      render() {
        return (
          <Container>
            <Input
              type="text"
              placeholder="Gimme your email!"
              onChange={this.updateEmail}
              value={this.state.email}
            />
          </Container>
        )
      }
    }
    

    参考 . 解决方案:https://github.com/styled-components/styled-components/issues/540#issuecomment-283664947

  • 12

    我刚刚遇到这个问题并来到这里寻求帮助 . 检查你的CSS!输入字段不能 user-select: none; ,否则无法在iPad上运行 .

  • 1

    我有同样的行为 .

    我的代码中的问题是我创建了一个嵌套的jsx元素数组,如下所示:

    const example = [
                [
                    <input value={'Test 1'}/>,
                    <div>Test 2</div>,
                    <div>Test 3</div>,
                ]
            ]
    
    ...
    
    render = () => {
        return <div>{ example }</div>
    }
    

    每次更新父元素时,此嵌套数组中的每个元素都会重新呈现 . 因此输入每次都会失去“ref”道具

    我修复了问题,将内部数组转换为反应组件(具有渲染函数的函数)

    const example = [
                <myComponentArray />
            ]
    
     ...
    
    render = () => {
        return <div>{ example }</div>
    }
    

    EDIT:

    当我构建一个嵌套的 React.Fragment 时会出现同样的问题

    const SomeComponent = (props) => (
        <React.Fragment>
            <label ... />
            <input ... />
        </React.Fragment>
    );
    
    const ParentComponent = (props) => (
        <React.Fragment>
            <SomeComponent ... />
            <div />
        </React.Fragment>
    );
    
  • 0

    核心原因是:当React重新渲染时,之前的DOM引用将无效 . 这意味着react已经改变了DOM树,你this.refs.input.focus将不起作用,因为这里的输入不再存在 .

  • 16

    autoFocus 属性应用于 input 元素可以在只有一个需要聚焦的输入的情况下执行变通方法 . 在这种情况下, key 属性是不必要的,因为它必须担心将 input 元素分解为它自己的组件,以避免失去对主要组件的重新渲染的关注 .

  • 0

    对我来说,这是由于 same 组件(称为UserList)中呈现的搜索输入框作为搜索结果列表引起的 . 因此,每当搜索结果发生更改时,整个UserList组件都会重新呈现,包括输入框 .

    我的解决方案是创建一个名为 UserListSearch 的全新组件,它与 UserList 分开 . 我不需要在UserListSearch的输入字段上设置键就可以了 . 我的UsersContainer的渲染功能现在看起来像这样:

    class UserContainer extends React.Component {
      render() {
        return (
          <div>
            <Route
              exact
              path={this.props.match.url}
              render={() => (
                <div>
                  <UserListSearch
                    handleSearchChange={this.handleSearchChange}
                    searchTerm={this.state.searchTerm}
                  />
                  <UserList
                    isLoading={this.state.isLoading}
                    users={this.props.users}
                    user={this.state.user}
                    handleNewUserClick={this.handleNewUserClick}
                  />
                </div>
              )}
            />
          </div>  
        )
      }
    }
    

    希望这对某人也有帮助 .

  • 1

    提供的答案对我没有帮助,这是我做的,但我有一个独特的情况 .

    为了清理代码,我倾向于使用这种格式,直到我准备将组件拉入另一个文件 .

    render(){
       const MyInput = () => {
          return <input onChange={(e)=>this.setState({text: e.target.value}) />
       }
       return(
          <div>
             <MyInput />
          </div>
       )
    

    但是当我将代码直接放在它工作的div中时,这会导致它失去焦点 .

    return(
       <div>
          <input onChange={(e)=>this.setState({text: e.target.value}) />
       </div>
    )
    

    我不知道为什么会这样,这是我用这种方式编写的唯一问题,而且我在大多数文件中都这样做,但是如果有人做了类似的事情,这就是它失去焦点的原因 .

  • 2

    结果我将 this 绑定到导致它重新渲染的组件 .

    我想我会在这里发布,以防其他人有这个问题 .

    我不得不改变

    <Field
        label="Post Content"
        name="text"
        component={this.renderField.bind(this)}
    />
    

    <Field
        label="Post Content"
        name="text"
        component={this.renderField}
    />
    

    简单修复,因为在我的情况下,我实际上并不需要在 renderField 中使用 this ,但希望我发布这个将有助于其他人 .

  • 0

    我将 value 道具切换为 defaultValue . 这对我行得通 .

    ...
    // before
    <input value={myVar} />
    
    // after
    <input defaultValue={myVar} />
    
  • 0

    我在html表中遇到了同样的问题,我在列中输入了文本行 . 在循环中我读了一个json对象和我创建行特别是我有一个带有inputtext的列 .

    http://reactkungfu.com/2015/09/react-js-loses-input-focus-on-typing/

    我设法通过以下方式解决它

    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>
                );
              }
            }
    

相关问题