Material-ui: [TextField] pierde el foco en la reproducción

Creado en 8 jun. 2015  ·  41Comentarios  ·  Fuente: mui-org/material-ui

Si setState vuelve a representar un campo de texto que tenía el foco, pierde el foco.

question v0.x

Comentario más útil

@scotmatson Puedo confirmar que, al menos en mi caso, el problema se resuelve si convierto el Container = styled.div que tengo como contenedor en un componente normal sin estilo div .

Editar:
Y también comparto tus frustraciones. Sería realmente bueno si estas dos bibliotecas funcionaran juntas sin problemas. Este problema y el problema de la especificidad son dos cosas que espero que se resuelvan pronto, pero estoy agradecido por las personas que ofrecen su tiempo como voluntarios.

Edición 2:
Acabo de descubrir qué estaba causando el problema en mi caso: ¡declarar componentes de componentes con estilo en el método de renderizado! Esto provocaba que los componentes se volvieran a crear en cada nueva renderización y React no podía rastrear qué elemento tenía el foco en el límite de la nueva renderización. Esto también me estaba dando algunos otros problemas de reproducción.

Mover todas mis declaraciones de componentes con estilo fuera de mi componente resolvió los problemas de pérdida de enfoque para mí. Probablemente un error de novato, pero nadie lo ha mencionado aquí, así que pensé en volver e informar.

Todos 41 comentarios

+1

Tengo que usar el evento onBlur con defaultValue para que los campos de texto funcionen sin problemas.

¿Alguien tiene una solución para que esto funcione?

Ya no creo que esto sea un problema. Ver código a continuación:

//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;


Aquí está la aplicación en acción (con el estado actualizado que se registra en la consola a medida que cambia la entrada de TextField). Puede ver que TextField conserva el foco:

4uo63bzhmj

Todavía entiendo esto. El ejemplo de
para reproducir el uso
<TextField ref="textField" value={this.state.valueToBeEdited} onChange = {this._incrementStateCounter}/>

o

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

También estoy viendo lo que parece ser una regresión de este problema. Mis TextFields están perdiendo el foco en la re-renderización. Estoy configurando el value prop del TextField , por lo que es un componente controlado.

Noto una nueva advertencia en la consola: 'TextField está cambiando una entrada no controlada de tipo de texto para ser controlado'. No recuerdo haber visto eso antes, no estoy seguro de si es relevante aquí o no. La advertencia aparece una vez que ingreso el primer carácter en TextField .

A través de una pequeña depuración, parece que algo está causando que TextField pierda el foco. Se invoca el controlador interno onBlur , que cambia el estado interno isFocused de TextField a falso. Si registro document.activeElement durante el controlador onBlur (para determinar qué componente ha robado el foco), registra la raíz del documento (cuerpo).

Pude reducir esto aún más determinando que la fuente del evento de desenfoque fue el primer MenuItem dentro de un Menu en otra parte de la aplicación. Establecer la propiedad disableAutoFocus en true resolvió mi problema. Espero que esto ayude a alguien más.

Abrí una edición separada para capturar esto: # 4387.

Puedo confirmar que esto sigue siendo un problema ...

Al verlo en un formulario de inicio de sesión muy simple, lo estoy usando junto con formularios redux y cuando interactúo con el formulario, provoca una nueva representación (tocó un campo en el lenguaje de forma redux) que pierde el enfoque.

Como solución temporal, solo volveré a renderizar el formulario real si hubo un cambio en el estado de error.

Puede reproducir este problema fácilmente poniendo TextField dentro de un Table establecido como selectable=true . Siempre que haga clic en un campo de texto para comenzar a editarlo, la línea se enfocará, por lo tanto, cambiará su color de fondo (supongo que algunos props de la línea podrían establecerse como selected=true ), por lo tanto desencadenar una re-renderización, por lo que perderá el enfoque ...

Así que básicamente no puedes usar un TextField dentro de un selectable Table , no estoy seguro de que sea una buena práctica de UX de todos modos, pero aún así :)

Notamos que en nuestro proyecto, puedo intentar presionar un reproductor.

¿Alguna solución a esto? También notando esto cuando se usa un área de entrada de texto normal dentro de una tabla.

@ dalexander01 Encontré una "solución", pero parece muy específica de la forma redux, ya que estaba experimentando el mismo problema cuando no usaba componentes de entrada md. Si es de alguna utilidad, puedo publicar un fragmento.

@deyceg por favor hazlo, no puede hacer daño 😄. Simplemente encuentre este problema con esta combinación de <Dialog/> y <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>

Lo resolví para mi caso, debe proporcionar una identificación única para ese campo de texto.
...
Sin embargo, la identificación no debería cambiar después de volver a renderizar. De esta manera, React puede realizar un seguimiento de qué elemento se centró o no.
PD: tenía campos de texto en una matriz que se representa a través de myArray.map, allí debe proporcionar las mismas claves al volver a procesar.

EDITAR: Probé de nuevo, solo tener la "clave" en myArray para que sea la misma en todas las reproducciones resolvió el problema.
La identificación del campo de texto cambió a 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));

¿Alguien logró encontrar una solución a este problema (sin usar refs )? Parece ser la combinación de onChange y establecer el prop value en una tecla de estado que hace que la entrada pierda el foco.

También tuve el siguiente problema, lo resolví tomando la implementación de SelectField si lo ve internamente, está llamando a DropdownMenu https://github.com/callemall/material-ui/blob/ccf712c5733508784cd709c18c29059542d6aad1/src/SelectField/SelectField. js # L192

así que también lo necesito para agregar un DropDownMenu, esto internamente está envolviendo los componentes en un menú, agregué disableAutoFocus para que mi TextField mantenga el enfoque en él como se muestra a continuación.

Tal vez podamos exponer un componente DropDownMenu desde SelectField y permitir que el enfoque automático se desactive con accesorios.

out

Una solución para esto para aquellos que están luchando con él y solo quieren que algo funcione, aunque es posible que no sea aplicable en algunas situaciones y podría presentar problemas (aunque no estoy del todo seguro) es usar una variable en la instancia en lugar de que el estado. Entonces, en lugar de this.state.value sería this.value que onChange se actualizaría. El componente no tendría un prop value . Luego, en el controlador onChange , usaría e.target.value .

La única diferencia que conozco entre una variable en la instancia y el estado es que la instancia var no activará una nueva renderización. Aunque podría haber otras razones, así que espero que otras personas me tomen el relevo en esta.

Sigo recibiendo este error. ¿Alguna solución?

La solución que encontré fue anular la función shouldComponentUpdate en mi componente y luego devolver falso cuando cambié los valores de estado utilizados por mis campos de texto.

`shouldComponentUpdate(nextProps, nextState) {    

    if(nextState.email != this.state.email) {  

      return false;  

    }

    else if(nextState.password != this.state.password) {  

      return false;  

    }  
    else return true;  
    }

'

Hacerlo de esta manera solucionó mi problema. El problema parece ser la representación del componente cada vez que cambia el estado, lo que luego restablece el enfoque. Entonces, al usar la función anterior para deshabilitar el renderizado cuando realiza un evento onChange para establecer valores de estado, el componente no se recarga.

@ Aspintyl000 esto funciona ... algo. Cuando establezco el valor del campo de texto a través de la propiedad 'valor', y establezco esa propiedad en 'this.state.value', no aparece nada. Aunque, cuando lo elimino, parece funcionar.

Estoy usando campos de texto en una fila de la tabla, y esto es un problema :( ¿algún progreso?

Psid.txt

También recibí el mismo error. Pero después de usar componentDidMount () {
this.refs.textField.focus ();
}
no pierde el foco. Pero no muestra el valor de entrada.

Intenté agregar <TextField /> en un <Table /> . Como en las situaciones anteriores, no puedo enfocar la entrada.
Mi solución: agregue un evento onClick al , cuando hago clic en él, establezco el selectable=false la tabla. Después de su operación de entrada, use un evento onBlue para establecer el selectable=true la tabla.

Comportamiento bastante desagradable.

Experimenté esto después de envolver dos cuadros de entrada con un componente con estilo. El div externo rompió completamente el comportamiento. Este es un defecto terrible en el diseño de esta biblioteca.

Así que esta es una solución bastante terrible, pero lo siguiente funciona, en su mayoría:

`` ``
onSomeValueChanged = evento => {
this.props.someValueChanged (event.target.value);

const { target } = event;
setTimeout(() => {
  target.focus();
}, 10);

};
`` ``

Sí, no es bonito ...

@scotmatson " styled-components .

@scotmatson Puedo confirmar que, al menos en mi caso, el problema se resuelve si convierto el Container = styled.div que tengo como contenedor en un componente normal sin estilo div .

Editar:
Y también comparto tus frustraciones. Sería realmente bueno si estas dos bibliotecas funcionaran juntas sin problemas. Este problema y el problema de la especificidad son dos cosas que espero que se resuelvan pronto, pero estoy agradecido por las personas que ofrecen su tiempo como voluntarios.

Edición 2:
Acabo de descubrir qué estaba causando el problema en mi caso: ¡declarar componentes de componentes con estilo en el método de renderizado! Esto provocaba que los componentes se volvieran a crear en cada nueva renderización y React no podía rastrear qué elemento tenía el foco en el límite de la nueva renderización. Esto también me estaba dando algunos otros problemas de reproducción.

Mover todas mis declaraciones de componentes con estilo fuera de mi componente resolvió los problemas de pérdida de enfoque para mí. Probablemente un error de novato, pero nadie lo ha mencionado aquí, así que pensé en volver e informar.

El problema que parece tener es que el enfoque es mucho cada vez que se representa un TextFields (o Input) dentro de un object.map (). Los TextFields fuera de los mapas funcionan perfectamente. ¿Me falta algún accesorio o algo más? Estoy totalmente perdido

Probé casi todas las soluciones sugeridas en este hilo sin suerte.

La estructura aproximada es esta

Archivo principal

...
    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>

NewUxCard

...
<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>

Vale la pena señalar que estoy usando Redux-React, por lo que los cambios de estado pasan por input-> action-> reducer

¿Algunas ideas? Gracias

EDITAR 1:
Una mayor investigación del problema, lo he reducido y resuelto eliminando la función de reacción Pasos (accesorios) que se usa para crear una etiqueta(en este caso).

Entonces, antes, la estructura era-> Pasos de función (apoyos) -> Clase de mapeo. Entonces, eliminé la función (el intermediario) y solo Const Steps -> Mapping Class. Solo puedo especular sobre por qué esto está funcionando (tal vez se ignoraron algunos accesorios) pero resolvió mi caso.

@piltsen Acabo de encontrar este problema en v1.5.0 mientras renderizaba TextFields en un .map() - Lo reduje a que mi clave TextField era el mismo valor que la entrada, por lo que cuando cambió probablemente causó que TextField se montara de nuevo y perder el enfoque. No estoy seguro de si eso te ayuda, aunque estoy seguro de que ya lo has solucionado.

Este también es un síntoma del uso de accesorios de renderizado. Haciendo algo como

<HigherOrderComponent
    render={hocProps => <TextField value={...} onChange={...} />
/>

puede causar este comportamiento. Para solucionarlo, pase un fragmento si lo permite el diseño:

<HigherOrderComponentThatDoesntPassOwnState
    component={<TextField  {...this.props}/>}
/>

En mi caso, parece que no se perdió el enfoque, pero el cursor desaparece de TextField. Puedo verificar que document.activeElement sea igual a mi TextField, puedo usar el comando del teclado y la rueda del mouse para cambiar el valor (número de tipo), pero cada ejecución del reductor de tipo se produce y el cursor se pierde.

Luego, en la parte superior de renderizado, busco por id mi TextField y hago

MyTextField.blur();
MyTextField.focus();

, que no está ayudando. Pero cuando hago focus () con un tiempo de espera , parece estar funcionando.

Editar:
Bueno, dentro de HOC todavía no funciona, por lo que parece necesario subirlo de todos modos.

@ younes0 Puedo confirmar que también me lo soluciona. En mi caso, tenía un componente auxiliar sin estado que devolvería un TextField con algunos conjuntos de accesorios comunes y luego distribuiría el resto de los accesorios en él, por lo que no tuve que especificar cosas como className o variant varias veces en todo el formulario. Mis campos de texto estaban perdiendo el foco tan pronto como se realizó algún cambio en el formulario, y al eliminar la función auxiliar y simplemente hacer que TextField s sin procesar una y otra vez lo arreglaran.

¿Se puede reabrir esto o se debe presentar una nueva emisión?

Sigo teniendo este problema, estoy usando 3.2.0

@OmarDavilaP ¿Puedes crear una nueva edición con una reproducción?

Lo resolví para mi caso, debe proporcionar una identificación única para ese campo de texto.
...
Sin embargo, la identificación no debería cambiar después de volver a renderizar. De esta manera, React puede realizar un seguimiento de qué elemento se centró o no.
PD: tenía campos de texto en una matriz que se representa a través de myArray.map, allí debe proporcionar las mismas claves al volver a procesar.

EDITAR: Probé de nuevo, solo tener la "clave" en myArray para que sea la misma en todas las reproducciones resolvió el problema.
La identificación del campo de texto cambió a 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));

Sí, resuelve el problema en mi caso, básicamente estaba usando:

<NewTextField key={user.id+Math.random()}>
Lo reemplacé por:

<NewTextField key={user.id}>
Entonces, sí, parece una mala práctica generar claves aleatorias cada vez que vuelve a renderizar el componente.

Para mí, el problema fue que creé una función contenedora para TextField, sin embargo, declaré esa función dentro de la función de mi componente, una vez que me moví fuera de mi función, ¡todo funcionó como se esperaba!

Creé una muestra de sendbox https://codesandbox.io/s/material-demo-msbxl?fontsize=14&hidenavigation=1&theme=dark

Tengo este problema con la representación condicional. Tengo un objeto javascript en el render que devuelve varios componentes en el archivo .js. No lo estoy describiendo correctamente, lo siento. Cuando lo arregle, editaré esta publicación, si me acuerdo.

editar: ¡Sí, lo arreglé! Creo que la forma en que estaba renderizando condicionalmente era renderizando cada componente incluso cuando no se mostraba. Moví la lógica fuera del render y parece funcionar bien ahora. Aquí está el código antiguo:

editar: no puedo en el estilo de esta cosa así que no importa.

Encontré este problema hoy y me golpeé la cabeza durante unas buenas horas. En mi caso, lo que estaba haciendo mal era envolver el TextField en un componente sin estado para organizar mejor mi página (no renderizar algunos si se están cargando datos, etc., etc.), pero dejé el useState devolución TextField en el componente principal.

Para la posteridad, aquí hay un pequeño enlace de codeandbox para reproducir mi estupidez: https://codesandbox.io/s/mui-textedit-losefocus-g6xxb

La pregunta ahora es, ¿cómo crearía mi componente personalizado envolviendo TextField ? Tanto HOC como createElement parecen incurrir en este error:

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

No importa, el enfoque anterior funciona bien: me di cuenta de que estaba definido el componente personalizado dentro del componente principal, lo que causó el problema. Cuando se mueve fuera del alcance principal (como debería ser), funciona bien: actualicé la caja de arena anterior para mostrar el enfoque correcto: https://codesandbox.io/s/mui-textedit-losefocus-g6xxb

Para mí, el problema fue que creé una función contenedora para TextField, sin embargo, declaré esa función dentro de la función de mi componente, una vez que me moví fuera de mi función, ¡todo funcionó como se esperaba!

Creé una muestra de sendbox https://codesandbox.io/s/material-demo-msbxl?fontsize=14&hidenavigation=1&theme=dark

Este era mi problema. ¡Gracias!

No importa, el enfoque anterior funciona bien: me di cuenta de que estaba definido el componente personalizado dentro del componente principal, lo que causó el problema. Cuando se mueve fuera del alcance principal (como debería ser), funciona bien: actualicé la caja de arena anterior para mostrar el enfoque correcto: https://codesandbox.io/s/mui-textedit-losefocus-g6xxb

Gracias. ¡Esto funcionó!

Para mí, el problema fue que creé una función contenedora para TextField, sin embargo, declaré esa función dentro de la función de mi componente, una vez que me moví fuera de mi función, ¡todo funcionó como se esperaba!

Creé una muestra de sendbox https://codesandbox.io/s/material-demo-msbxl?fontsize=14&hidenavigation=1&theme=dark

@ fleischman718 ¡ gracias por descubrir este! ¡La eliminación de la función del componente de envoltura para un TextField dentro de la definición de un componente principal solucionó mi problema!

Me tomó un segundo averiguar qué estaba pasando en la caja de arena, así que pongo un resumen a continuación:

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>
  )
}
¿Fue útil esta página
0 / 5 - 0 calificaciones