Se um campo de texto que tinha foco é renderizado novamente por setState
, ele perde o foco.
+1
Tenho que usar o evento onBlur com defaultValue para fazer os Textfields funcionarem perfeitamente.
Alguém tem uma solução alternativa para fazer isso funcionar?
Não acho mais isso um problema. Veja o código abaixo:
//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;
Aqui está o aplicativo em ação (com o estado atualizado sendo registrado no console conforme a entrada TextField muda). Você pode ver que o TextField mantém o foco:
Eu ainda entendo isso. O exemplo
para reproduzir usar
<TextField ref="textField" value={this.state.valueToBeEdited} onChange = {this._incrementStateCounter}/>
ou
<TextField ref="textField" defaultValue={this.state.valueToBeEdited} onChange = {this._incrementStateCounter}/>
Também estou vendo o que parece ser uma regressão desse problema. Meus TextFields estão perdendo o foco na re-renderização. Estou configurando o value
prop de TextField
, portanto, é um componente controlado.
Noto um novo aviso no console: 'TextField está alterando uma entrada não controlada do tipo de texto a ser controlado'. Não me lembro de ter visto isso antes, não tenho certeza se é relevante aqui ou não. O aviso aparece quando eu insiro o primeiro caractere em TextField
.
Por meio de uma pequena depuração, parece que algo está fazendo com que TextField
perca o foco. A interna onBlur
manipulador é invocado, que alterna o isFocused
estado interno do TextField
para falso. Se eu logar document.activeElement
durante o manipulador onBlur
(para determinar qual componente roubou o foco), ele registra a raiz do documento (corpo).
Consegui diminuir ainda mais determinando que a origem do evento de desfoque foi o primeiro MenuItem
em Menu
em outro lugar no aplicativo. Definir a propriedade disableAutoFocus
como true
resolveu meu problema. Espero que isso ajude mais alguém.
Abri uma edição separada para capturar isso: # 4387.
Posso confirmar que isso ainda é um problema ...
Vendo isso em um formulário de login muito simples - estou usando isso em conjunto com os formulários redux e quando interajo com o formulário, ele causa uma re-renderização (tocou um campo na linguagem redux-form) que perde o foco.
Como solução alternativa, só estou renderizando novamente o formulário real se houver uma alteração no estado de erro.
Você pode reproduzir esse problema facilmente colocando TextField
em Table
definido como selectable=true
. Sempre que você clicar em um campo de texto para começar a editá-lo, a linha receberá o foco, mudando sua cor de fundo (acho que alguns props
da linha podem ser definidos como selected=true
), acione uma nova renderização, assim você perderá o foco ...
Então, você basicamente não pode usar TextField
dentro de selectable
Table
, não tenho certeza se é uma boa prática de UX de qualquer maneira, mas ainda assim :)
Percebemos que em nosso projeto, posso tentar empurrar um reprodutor.
Qualquer solução para isso? Observe isso também ao usar uma área de texto de entrada normal dentro de uma tabela.
@ dalexander01 Encontrou uma "solução", mas parece muito específica do redux-form, pois estava tendo o mesmo problema ao não usar componentes de entrada MD. Se for útil, posso postar um snippet!
@deyceg por favor, não faz mal 😄. Basta encontrar esse problema com esta combinação de <Dialog/>
e <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>
Eu resolvi para o meu caso, você precisa fornecer um id exclusivo para esse campo de texto.
O id não deve mudar após a renderização. Dessa forma, o React pode rastrear qual elemento foi focalizado ou não.
PS Eu tinha textfields em um array que é renderizado via myArray.map, lá você tem que fornecer as mesmas chaves ao renderizar.
EDIT: Eu testei novamente, apenas ter a "chave" em myArray para ser a mesma em todos os renderizadores resolveu o problema.
O id do campo de texto mudou para 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));
Alguém conseguiu encontrar uma solução alternativa para esse problema (sem usar refs
)? Parece ser a combinação de onChange
e definir o value
prop para uma chave de estado que faz com que a entrada perca o foco.
Eu também tive o seguinte problema. Resolvi-o tendo a implementação de SelectField se você vê-lo internamente chamando um DropdownMenu https://github.com/callemall/material-ui/blob/ccf712c5733508784cd709c18c29059542d6aad1/src/SelectField/SelectField. js # L192
então eu também preciso adicionar um DropDownMenu que internamente envolve os componentes em um Menu, adicionei disableAutoFocus para fazer meu TextField manter o foco nele como mostrado abaixo.
Talvez possamos expor um componente DropDownMenu do SelectField e permitir que o foco automático seja desativado com adereços.
Uma solução alternativa para isso para aqueles que estão lutando com isso e apenas querem que algo funcione, embora seja possível que não seja aplicável em algumas situações e possa apresentar problemas (eu não estou totalmente certo de mim mesmo) é usar uma variável na instância, em vez de do que o estado. Então, em vez de this.state.value
seria this.value
que onChange
estaria atualizando. O componente não teria um prop value
. Então, no manipulador onChange
você usaria e.target.value
.
A única diferença que conheço entre uma variável na instância e o estado é que a instância var não acionará uma nova renderização. Embora possa haver outros motivos, então estou esperando que os outros me ajudem neste.
Ainda estou recebendo este erro. Qualquer solução alternativa?
A solução alternativa que encontrei foi substituir a função shouldComponentUpdate para meu componente e, em seguida, retornar false quando alterei os valores de estado usados por meus 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;
}
`
Fazer assim resolveu meu problema. O problema parece ser a renderização do componente toda vez que o estado muda, o que redefine o foco. Portanto, usando a função acima para desabilitar a renderização quando você faz um evento onChange para definir valores de estado, o componente não recarrega.
@ Aspintyl000 isso funciona ... um pouco. Quando estou configurando o valor do campo de texto por meio da prop 'valor' e estou configurando essa prop para 'this.state.value', nada aparece. Embora, quando eu removo isso, pareça funcionar.
Estou usando textfields em uma linha de tabela, e isso é um problema :( algum progresso?
Eu também estava recebendo o mesmo erro. Mas depois de usar componentDidMount () {
this.refs.textField.focus ();
}
não perde o foco. Mas não mostra o valor de entrada.
Tentei adicionar <TextField />
em um <Table />
. Como nas situações acima, não consigo focar a entrada.
Minha solução: adicionar um evento onClick
aoselectable=false
da mesa. Após sua operação de entrada, use um evento onBlue
para definir o selectable=true
da mesa.
Comportamento bastante desagradável.
Eu tinha experimentado isso depois de envolver duas caixas de entrada com um componente estilizado. O div externo quebrou completamente o comportamento. Esta é uma falha terrível no design desta biblioteca.
Portanto, esta é uma solução bastante terrível, mas o seguinte funciona, principalmente:
`` ``
onSomeValueChanged = event => {
this.props.someValueChanged (event.target.value);
const { target } = event;
setTimeout(() => {
target.focus();
}, 10);
};
`` ``
Sim, não é bonito ...
@scotmatson " styled-components
.
@scotmatson Posso confirmar que, pelo menos no meu caso, o problema será resolvido se eu transformar o Container = styled.div
que tenho como invólucro em um componente normal sem estilo div
.
Editar:
E também compartilho suas frustrações. Seria muito bom se essas duas bibliotecas funcionassem juntas na perfeição. Esta questão e a questão da especificidade são duas coisas que espero que sejam resolvidas em breve, mas sou grato pelas pessoas que dedicaram seu tempo.
Edit2:
Acabei de descobrir o que estava causando o problema no meu caso: declarar componentes de componentes estilizados no método de renderização! Isso estava fazendo com que os componentes fossem recriados em cada renderização novamente e o React não conseguia rastrear qual elemento estava em foco no limite da renderização. Isso também estava levando a alguns outros problemas de renderização para mim.
Mover todas as minhas declarações de componentes estilizados para fora do meu componente resolveu os problemas de perda de foco para mim. Provavelmente um erro noob, mas ninguém mencionou isso aqui, então pensei em voltar e relatar.
O problema que parece que estou tendo é que o foco é muito sempre que um TextFields (ou Input) está sendo renderizado dentro de um object.map (). Os TextFields fora dos mapas estão funcionando perfeitamente. Estou perdendo algum adereço ou qualquer outra coisa? Estou totalmente perdida
Tentei quase todas as soluções sugeridas neste tópico sem qualquer sorte.
A estrutura bruta é esta
Arquivo 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 a pena notar que estou usando Redux-React, então as mudanças de estado passam por entrada-> ação-> redutor
Alguma ideia? obrigado
EDIT 1:
Uma investigação mais aprofundada do problema, reduzi-o e resolvi removendo a função de reação Steps (props) que é usada para criar uma tag
Antes, a estrutura era
@piltsen Acabei de encontrar esse problema na v1.5.0 ao renderizar TextFields em um .map()
- reduzi-o para que minha chave TextField tivesse o mesmo valor da entrada, então, quando mudou, provavelmente causou a montagem do TextField novamente e perder o foco. Não tenho certeza se isso ajuda você - embora eu tenha certeza que agora você já resolveu
Este também é um sintoma do uso de acessórios de renderização. Fazendo algo como
<HigherOrderComponent
render={hocProps => <TextField value={...} onChange={...} />
/>
pode causar esse comportamento. Para corrigir, passe um fragmento, se permitido pelo design:
<HigherOrderComponentThatDoesntPassOwnState
component={<TextField {...this.props}/>}
/>
No meu caso, parece que o foco não foi perdido, mas o cursor desapareceu do TextField. Posso verificar se document.activeElement é igual ao meu TextField, posso usar o comando do teclado e a roda do mouse para alterar o valor (digite o número), mas cada renderização do redutor de tipo acontece e o cursor perde.
Então, no topo da renderização, procuro por id meu TextField e faço
MyTextField.blur();
MyTextField.focus();
, o que não está ajudando. Mas quando faço focus () com algum timeout , parece estar funcionando.
Editar:
Bem, dentro do HOC ele ainda não está funcionando, então parece que é necessário aumentá-lo de qualquer maneira.
@ younes0 Posso confirmar que isso também corrige para mim. No meu caso, eu tinha um componente sem estado auxiliar que retornaria um TextField com alguns adereços comuns definidos e, em seguida, espalharia o resto dos adereços nele, então não tive que especificar coisas como className
ou variant
várias vezes em todo o formulário. Meus campos de texto estavam perdendo o foco assim que qualquer alteração era feita no formulário e removendo a função auxiliar e apenas fazendo TextField
s brutos repetidamente consertando isso.
Isso pode ser reaberto ou um novo fascículo deve ser arquivado?
Ainda estou tendo esse problema, estou usando o 3.2.0
@OmarDavilaP Você pode criar um novo exemplar com uma reprodução?
Eu resolvi para o meu caso, você precisa fornecer um id exclusivo para esse campo de texto.
...
O id não deve mudar após a renderização. Dessa forma, o React pode rastrear qual elemento foi focalizado ou não.
PS Eu tinha textfields em um array que é renderizado via myArray.map, lá você tem que fornecer as mesmas chaves ao renderizar.EDIT: Eu testei novamente, apenas ter a "chave" em myArray para ser a mesma em todos os renderizadores resolveu o problema.
O id do campo de texto mudou para 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));
Sim, isso resolve o problema no meu caso, basicamente, eu estava usando:
<NewTextField key={user.id+Math.random()}>
Eu substituí por:
<NewTextField key={user.id}>
Então, sim, parece uma prática ruim gerar chaves aleatórias cada vez que ele renderizar novamente o componente.
Para mim, o problema era, eu criei uma função de wrapper para TextField, no entanto, declarei essa função dentro da minha função de componente, uma vez movido para fora da minha função, tudo funcionou como esperado!
Criei um exemplo de caixa de envio https://codesandbox.io/s/material-demo-msbxl?fontsize=14&hidenavigation=1&theme=dark
Estou tendo esse problema com a renderização condicional. Eu tenho um objeto javascript na renderização que retorna vários componentes no arquivo .js. Não estou descrevendo corretamente, desculpe. Quando eu consertar, vou editar este post, se me lembrar.
editar: Yay, consertei! Acredito que a maneira como eu estava renderizando condicionalmente estava renderizando cada componente mesmo quando não era exibido. Mudei a lógica para fora do render e parece funcionar bem agora. Aqui está o código antigo:
editar: não consigo estilizar essa coisa, então deixa pra lá.
Eu encontrei esse problema hoje e bati minha cabeça nele por algumas horas. No meu caso, o que eu estava fazendo de errado foi envolver TextField
em um componente sem estado para organizar melhor minha página (não renderizar se os dados estiverem carregando, etc etc), mas deixei useState
callback que controlava TextField
no componente pai.
Para a posteridade, aqui está um pequeno link codesandbox para reproduzir minha estupidez: https://codesandbox.io/s/mui-textedit-losefocus-g6xxb
A questão agora é: como eu criaria meu envoltório de componente personalizado TextField
? Ambos HOC e createElement
parecem incorrer neste bug:
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}
});
};
Esqueça, a abordagem acima funciona bem - percebi que havia definido o componente personalizado dentro do componente pai, o que causou o problema. Quando movido para fora do escopo pai (como deveria ser), funciona bem - atualizei a sandbox anterior para mostrar a abordagem correta: https://codesandbox.io/s/mui-textedit-losefocus-g6xxb
Para mim, o problema era, eu criei uma função de wrapper para TextField, no entanto, declarei essa função dentro da minha função de componente, uma vez movido para fora da minha função, tudo funcionou como esperado!
Criei um exemplo de caixa de envio https://codesandbox.io/s/material-demo-msbxl?fontsize=14&hidenavigation=1&theme=dark
Esse era o meu problema. Obrigado!
Esqueça, a abordagem acima funciona bem - percebi que havia definido o componente personalizado dentro do componente pai, o que causou o problema. Quando movido para fora do escopo pai (como deveria ser), funciona bem - atualizei a sandbox anterior para mostrar a abordagem correta: https://codesandbox.io/s/mui-textedit-losefocus-g6xxb
Obrigado. Isso funcionou!
Para mim, o problema era, eu criei uma função de wrapper para TextField, no entanto, declarei essa função dentro da minha função de componente, uma vez movido para fora da minha função, tudo funcionou como esperado!
Criei um exemplo de caixa de envio https://codesandbox.io/s/material-demo-msbxl?fontsize=14&hidenavigation=1&theme=dark
@ fleischman718 obrigado por descobrir isso! Remover a função do componente de agrupamento para um TextField dentro da definição de um componente pai corrigiu meu problema!
Levei um segundo para descobrir o que estava acontecendo na caixa de areia, então coloquei um resumo abaixo:
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>
)
}
Comentários muito úteis
@scotmatson Posso confirmar que, pelo menos no meu caso, o problema será resolvido se eu transformar o
Container = styled.div
que tenho como invólucro em um componente normal sem estilodiv
.Editar:
E também compartilho suas frustrações. Seria muito bom se essas duas bibliotecas funcionassem juntas na perfeição. Esta questão e a questão da especificidade são duas coisas que espero que sejam resolvidas em breve, mas sou grato pelas pessoas que dedicaram seu tempo.
Edit2:
Acabei de descobrir o que estava causando o problema no meu caso: declarar componentes de componentes estilizados no método de renderização! Isso estava fazendo com que os componentes fossem recriados em cada renderização novamente e o React não conseguia rastrear qual elemento estava em foco no limite da renderização. Isso também estava levando a alguns outros problemas de renderização para mim.
Mover todas as minhas declarações de componentes estilizados para fora do meu componente resolveu os problemas de perda de foco para mim. Provavelmente um erro noob, mas ninguém mencionou isso aqui, então pensei em voltar e relatar.