web-dev-qa-db-fra.com

React.js - entrée perdant le focus lors du rendu

Je suis juste en train d'écrire en saisie de texte et dans onChange, j'appelle setState, donc React redirige mon interface utilisateur. Le problème est que la saisie de texte perd toujours un focus, je dois donc le focaliser à nouveau pour chaque lettre: 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>
        );
    }

});
39
Krab

Sans voir le reste de votre code, il s'agit d'une estimation ..___. Lorsque vous créez un EditorContainer, spécifiez une clé unique pour le composant:

<EditorContainer key="editor1"/>

Lorsqu'un re-rendu se produit, si la même clé est vue, cela indique à React de ne pas obstruer et de régénérer la vue, mais de le réutiliser. Ensuite, l'élément ciblé doit rester actif.

44
z5h

S'il s'agit d'un problème dans un routeur de réaction, <Route/>, utilisez la propriété render au lieu de component.

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


La perte de concentration se produit parce que la propriété component utilise React.createElement à chaque fois au lieu de simplement restituer les modifications.

Détails ici: https://reacttraining.com/react-router/web/api/Route/component

16
spencer.sm

Je reviens encore et encore et je trouve toujours la solution à mon problème ailleurs à la fin… Donc, je vais la documenter ici parce que je sais que je l'oublierai encore!

La raison pour laquelle input perdait de son attention dans mon cas était due au fait que je rendais de nouveau le input sur le changement d'état.

Code buggy:

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

Donc, le problème est que je commence toujours à tout coder à un endroit pour le tester rapidement, puis le diviser en modules distincts . .

La correction est simple, faites la modularisation depuis le début, autrement dit, "déplacez le composant Input de la fonction render"

Code fixe

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

Réf. à la solution: https://github.com/styled-components/styled-components/issues/540#issuecomment-283664947

10
Deepak

Ma réponse est similaire à celle de @ z5h.

Dans mon cas, j'ai utilisé Math.random() pour générer un key unique pour le composant.

Je pensais que la variable key n'était utilisée que pour déclencher une restitution pour ce composant particulier plutôt que de restituer tous les composants de ce tableau (je retourne un tableau de composants dans mon code). Je ne savais pas qu'il est utilisé pour restaurer l'état après la restitution.

Enlever ça a fait le travail pour moi.

PS: J'aurais pu commenter cela, mais je n'ai pas assez de réputation pour ça.

5

J'ai le même comportement.

Le problème dans mon code est que j'ai créé un tableau imbriqué d'éléments jsx comme ceci:

const example = [
            [
                <input value={'Test 1'}/>,
                <div>Test 2</div>,
                <div>Test 3</div>,
            ]
        ]

...

render = () => {
    return <div>{ example }</div>
}

Chaque élément de ce tableau imbriqué est rendu à chaque fois que j'ai mis à jour l'élément parent. Et donc les entrées perdent là "ref" prop à chaque fois

J'ai corrigé le problème avec la transformation du tableau interne en un composant de réaction (Une fonction avec une fonction de rendu)

const example = [
            <myComponentArray />
        ]

 ...

render = () => {
    return <div>{ example }</div>
}

MODIFIER:

Le même problème apparaît lorsque je crée un React.Fragment imbriqué

const SomeComponent = (props) => (
    <React.Fragment>
        <label ... />
        <input ... />
    </React.Fragment>
);

const ParentComponent = (props) => (
    <React.Fragment>
        <SomeComponent ... />
        <div />
    </React.Fragment>
);
2
chrisheyn

Je viens de rencontrer ce problème et je suis venu ici pour obtenir de l'aide. Vérifiez votre CSS! Le champ de saisie ne peut pas avoir user-select: none; ou ne fonctionnera pas sur un iPad.

2
David Sinclair

L'application de l'attribut autoFocus à l'élément input peut constituer une solution de contournement dans les cas où une seule entrée doit être ciblée. Dans ce cas, un attribut key serait inutile car il ne s'agit que d'un élément. De plus, vous n'aurez pas à craindre de diviser l'élément input en son propre composant pour éviter de perdre le focus sur le rendu du composant principal.

1
spakmad

La raison principale est la suivante: lorsque React restitue, votre précédente référence DOM sera invalide. Cela signifie que réagit a changé l’arborescence DOM et que ceci.refs.input.focus ne fonctionnera pas, car l’entrée n’existe plus.

1
hlissnake

Pour moi, cela était dû au fait que la zone de saisie de la recherche était rendue dans le composant même (appelé UserList) en tant que liste des résultats de la recherche. Ainsi, chaque fois que les résultats de la recherche sont modifiés, l'ensemble du composant UserList est renvoyé, y compris la zone de saisie.

Ma solution consistait à créer un tout nouveau composant appelé UserListSearch qui est séparé de UserList . Je n'avais pas besoin de définir des clés sur les champs de saisie dans UserListSearch pour que cela fonctionne. La fonction de rendu de mon UsersContainer ressemble maintenant à ceci:

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

Espérons que cela aide aussi quelqu'un.

1
Dom Eden

Les réponses fournies ne m'ont pas aidé, voici ce que j'ai fait mais j'avais une situation unique. 

Pour nettoyer le code, j'ai tendance à utiliser ce format jusqu'à ce que je sois prêt à extraire le composant dans un autre fichier. 

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

Mais cela l'a fait perdre le focus, lorsque j'ai mis le code directement dans le div cela a fonctionné. 

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

Je ne sais pas pourquoi, c’est le seul problème que j’ai eu à l’écrire de cette façon et je le fais dans la plupart des fichiers que j’ai, mais si quelqu'un fait la même chose, c’est la raison pour laquelle il perd le focus. 

0
Kevin Danikowski

J'ai eu le même problème avec une table HTML dans laquelle j'ai des lignes de texte dans une colonne. Dans une boucle, je lis un objet JSON et je crée des lignes, en particulier, j'ai une colonne avec inputtext.

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

J'ai réussi à le résoudre de la manière suivante

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>
            );
          }
        }
0
Software Factory

Mon problème était que j'ai nommé ma clé dynamiquement avec une valeur de l'item, dans mon cas "nom" donc la clé était key = {${item.name}-${index}}. Donc, lorsque je voulais changer l'entrée avec item.name comme valeur, la clé était également modifiée et donc, la réaction ne reconnaissait pas cet élément

0
Amogu

Il s'avère que je liais this au composant qui le rendait.

Je pensais le poster ici au cas où quelqu'un d'autre aurait ce problème.

Je devais changer

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

À 

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

Solution simple car dans mon cas, je n'avais pas besoin de this dans renderField, mais j'espère que le fait de poster ceci aidera quelqu'un d'autre.

0
Matt D

J'ai changé value prop en defaultValue. Ça marche pour moi.

...
// before
<input value={myVar} />

// after
<input defaultValue={myVar} />
0
chrisheyn