React: O evento de mudança dispara mais vezes antes do término da composição do IME

Criado em 21 mai. 2015  ·  48Comentários  ·  Fonte: facebook/react

Detalhes extras


Problema Original

Quando eu estava tentando este exemplo em https://facebook.github.io/react/blog/2013/11/05/thinking-in-react.html , quaisquer caracteres chineses inseridos pelo método de entrada pinyin chinês disparariam muitos renderizadores, como :

screen shot 2015-05-21 at 14 04 36

Na verdade, eu esperaria que eles não disparassem antes de eu confirmar o caractere chinês.

Então tentei outro tipo de método de entrada - método de entrada wubi, eu consegui isso:

screen shot 2015-05-21 at 14 17 15

É estranho também. Então, fiz um teste no jQuery :

screen shot 2015-05-21 at 14 05 12

Somente depois de pressionar a barra de espaço para confirmar o caractere, o evento keyup seria disparado.

Eu sei que pode ser diferente entre a implementação de jQuery keyup e a reação onChange , mas eu esperaria a maneira como a jQuery keyup lida com caracteres chineses em vez de onChange de reação

DOM Bug

Comentários muito úteis

Olá, pessoal do Facebook, na verdade este problema causa um problema SÉRIO : não podemos atualizar a entrada de forma assíncrona com a entrada em chinês.
Por exemplo, não podemos usar fontes de dados reativas de meteoros ou armazenamentos como redux, porque todos os comentários são atualizados de forma assíncrona.
Aqui está um exemplo mais simples para mostrar esse problema, use setTimeout para fazer a atualização assíncrona:
https://jsfiddle.net/liyatang/bq6oss6z/1/

Eu realmente espero que você possa corrigir isso rapidamente, para que não percamos esforços para contornar isso aqui e ali e novamente.

Obrigado.

Aqui está minha solução alternativa . Se alguém enfrentar o mesmo problema, você pode dar uma olhada

Todos 48 comentários

cc @salier :) - O que devemos fazer aqui?

Acho que não devemos disparar onChange até que a string IME seja confirmada.

Uma maneira de lidar com isso em ChangeEventPlugin seria ignorar todos os eventos input entre compositionstart e compositionend , em seguida, usar o evento input imediatamente seguindo compositionend .

Fiz alguns testes rápidos no OSX Chrome e Firefox com Simplified Pinyin e 2-Set Korean, e a ordem do evento e os dados parecem corretos o suficiente. (Prevejo que teremos problemas com o IE coreano, mas podemos ter sorte.)

Acho que podemos continuar a ver problemas com métodos de entrada alternativos, como a extensão do Google Input Tools, mas pode haver soluções alternativas para isso.

Isso também influencia como os caracteres dialéticos são digitados para as línguas latinas. Até mesmo pressionar e e usar a variante está falhando aqui.

Desculpe, isso parece não estar relacionado. Me desculpe.

Existe alguma atualização? Sofrendo com esse problema também.

Nenhum atualmente - esta não é uma alta prioridade para nós agora. Ficaria feliz em ver uma solicitação pull, se alguém se empenhar em consertar isso.

@salier Parece que o IE não dispara o evento input após compositionend . Eu testei no IE11 e Edge no Windows 10. Ele dispara corretamente no Chrome e Firefox.

no exemplo 9, o evento Change dispara muitas vezes ao inserir caracteres chineses novamente

Olá, pessoal do Facebook, na verdade este problema causa um problema SÉRIO : não podemos atualizar a entrada de forma assíncrona com a entrada em chinês.
Por exemplo, não podemos usar fontes de dados reativas de meteoros ou armazenamentos como redux, porque todos os comentários são atualizados de forma assíncrona.
Aqui está um exemplo mais simples para mostrar esse problema, use setTimeout para fazer a atualização assíncrona:
https://jsfiddle.net/liyatang/bq6oss6z/1/

Eu realmente espero que você possa corrigir isso rapidamente, para que não percamos esforços para contornar isso aqui e ali e novamente.

Obrigado.

Aqui está minha solução alternativa . Se alguém enfrentar o mesmo problema, você pode dar uma olhada

Fiz um exemplo simples para demonstrar como usar compositionstart e compositionend eventos para evitar a entrada de IME chinês em um problema de evento onchange .
Aqui está o link: https://jsfiddle.net/eyesofkids/dcxvas28/8/

@eyesofkids bom trabalho, isso poderia ser feito como a implementação padrão de onChange para entrada, textarea ...

bom trabalho !

Eu estava tendo o mesmo problema e a solução alternativa do @eyesofkids funciona perfeitamente (obrigado!).

Depois de implementar a solução alternativa, eu estava mergulhando no código-fonte do React para pelo menos tentar adicionar um teste com falha para isso - esperando adicionar mais tarde o comportamento esperado à biblioteca - embora pareça um pouco complicado para alguém não familiarizado com os componentes internos.

Inicialmente, esperava que um teste semelhante ao que já está disponível para ChangeEventPlugin deveria funcionar, ou seja, simular um compositionStart / compositionUpdate nativo e não verificar nenhum retorno de chamada onChange disparamos; também verificar onChange só seria disparado quando compositionEnd fosse simulado. No entanto, isso não parece funcionar.

Portanto, eu estava pensando que talvez verificar ChangeEventPlugin.extractEvents() seria uma abordagem viável, semelhante ao que foi feito nos testes para SelectEventPlugin . Aqui, por alguma razão, eu sempre recebo undefined ao extrair os eventos.
Para referência, este é o código de teste que tentei em _ChangeEventPlugin-test.js_:

  var EventConstants = require('EventConstants');
  var ReactDOMComponentTree = require('ReactDOMComponentTree');
  var topLevelTypes = EventConstants.topLevelTypes;

  function extract(node, topLevelEvent) {
    return ChangeEventPlugin.extractEvents(
      topLevelEvent,
      ReactDOMComponentTree.getInstanceFromNode(node),
      {target: node},
      node
    );
  }

  function cb(e) {
    expect(e.type).toBe('change');
  }
  var input = ReactTestUtils.renderIntoDocument(
    <input onChange={cb} value='foo' />
  );

  ReactTestUtils.SimulateNative.compositionStart(input);

  var change = extract(input, topLevelTypes.topChange);
  expect(change).toBe(null);

Receio não saber exatamente como se deve depurar esses testes - caso contrário, teria uma imagem mais clara do que está acontecendo. Qualquer orientação sobre como proceder ou quaisquer outras dicas seriam muito apreciadas.

A solução repentinamente quebrou no Chrome 53+ e parece que não é mais válido porque eles mudaram a ordem de compositionend é disparado : anteriormente isso acontecia antes de textInput , agora depois de textInput . Como consequência disso, change não será disparado se for abortado durante a composição 😕.

Existe uma solução complicada para o Chrome v53. Para chamar o handlechange após compositionend ser disparado.

handleComposition  = (event) => {

    if(event.type === 'compositionend'){
      onComposition = false

      //fire change method to update for Chrome v53
      this.handleChange(event)

    } else{
      onComposition = true
    }
  }

verifique a demonstração aqui: https://jsfiddle.net/eyesofkids/dcxvas28/11/

@chenxsan você descobriu a solução?
você pode detectar o compositionStart e deixar uma variável igual a true.
Em seguida, para usar a variável, que você definiu, em onChange para ver se ela deve disparar a consulta

Eu enviei um novo problema para componentes controlados em # 8683

A solução temporária para componentes não controlados e controlados (entrada, área de texto) é carregada para o evento de composição de reação .

@yesmeck muito feliz em ver esta notícia.

Eu vi o teste focar apenas no Webkit, ele deve ser separado em Chrome e Safari porque o Chrome muda sua ordem de compositionend acionada por evento após 53+.

@eyesofkids Adicionado um novo caso de teste para Chrome com menos de 53 anos.

Só para colocar lenha na fogueira, estou tentando contornar esse problema e descobri que a versão atual do iOS safari não aciona o evento compositionend ao usar o IME Hiragana japonês, acho que isso é intencional, pois o menu de composição parece nunca estar fechado.
No exemplo de solução alternativa @eyesofkids, o inputValue nunca é atualizado, embora para mim https://github.com/zhaoyao91/react-optimistic-input corrija o problema com o IME japonês.

Para quem procura uma solução para isso, aqui está um componente pronto para uso. https://github.com/aprilandjan/react-starter/blob/test/search-input/src/components/SearchInput.js Basta usá-lo em vez do elemento de entrada de texto normal e está tudo ok.

@ zhaoyao91 sua solução alternativa simplesmente funciona! muito obrigado.

ei pessoal alguma novidade nessa edição?

Não tem sido uma prioridade alta porque os disparos de onChange com muita frequência raramente causam problemas. Onde isso está causando problemas em seu aplicativo?

@sophiebits desculpe clicou acidentalmente no 'X'. Isso pode degradar o desempenho se houver operações de filtragem ou retornos de chamada do servidor usados ​​nos manipuladores de eventos de mudança. A abordagem mostrada em https://github.com/facebook/react/issues/3926#issuecomment -316049951 é uma boa solução alternativa para entradas não controladas ou nativas, mas não mapeia bem para entradas controladas pelo React. Parece que alguns neste tópico tentaram desenvolver um PR, mas acharam os aspectos internos um pouco complexos - mas talvez um engenheiro da sua equipe pudesse fazer o trabalho mais rápido? https://github.com/facebook/react/issues/8683 é uma descrição muito melhor do problema real da IMO.

Alguém pode me ajudar a entender: o problema está estritamente em onChange chamadas extras no meio ? Ou você obtém um valor incorreto no final?

O teste da tentativa de correção em https://github.com/facebook/react/pull/8438 passa se eu remover a afirmação sobre o número de vezes que onChange é chamado. Portanto, suponho que esse problema seja apenas sobre as onChange chamadas extras.

não há chamadas onChange extras, é apenas obter o valor incorreto no final, parece mais um problema onComposition.

@crochefluid Você pode criar um teste de falha para isso? Semelhante ao que # 8438 tentou fazer. Nesse teste, não houve valor incorreto.

@gaearon , vou tentar. Você tentou esse teste no safari (mac / IOS)?

É um teste de Nó, mas codifica sequências capturadas de diferentes navegadores e dispositivos. Por favor, veja sua fonte. Você precisaria adicionar sequências que estão falhando.

Portanto, suponho que esse problema seja apenas sobre as chamadas onChange extras.

Exatamente.

Ainda estou tendo esse problema. Parece que este problema está aberto há 3 anos. O React oferece suporte a entrada chinesa em componentes controlados no momento?

Também vendo isso em japonês com certos caracteres ...

Aqui está uma caixa de proteção de código que reproduz meu problema. Parece que está relacionado a formulários. Usar entradas diretamente está bom.

https://codesandbox.io/s/0m1760xqnl

Eu adicionei alguns casos:
Usar o estado de reação e entradas simples é bom
Usar o estado de reação, formulários simples e entradas simples é bom
Estamos usando um componente de formulário baseado em contexto que não está funcionando. Pode ser um problema relacionado ao contexto.

Problema resolvido: comecei a trabalhar no codepen. Por alguma razão, passar 'input' como um componente funcionou, ao passar (props) => nao fiz.

Alguém tem ideia de qual é a diferença?

Na verdade, eu também tentei:

Trabalho

<Field {...otherProps} component="input" />

Não funciona

<Field {...otherProps} component={(props) => <input {...props} />} />

Funciona estranhamente

const WrappedInput = (props) => <input {...props} />
...
<Field {...otherProps} component={WrappedInput} />

É claro que há alguma mágica acontecendo aqui que eu não entendo. 😕

Alguma atualização?

Parece causar resultado incorreto quando o IME está habilitado

e84721f3ec71a5ce043ef8290

Eu experimentei o mesmo problema que @otakustay
Parece impossível oferecer suporte a entrada controlada com entrada IME. Eu tracei a sequência de eventos para o seguinte.

  1. O usuário digita uma letra, digamos w
  2. onChange é acionado
  3. Estado é atualizado com novo valor
  4. O novo valor é propagado para input por meio do atributo value .
  5. A "composição" do IME é interrompida neste ponto

    • Existe uma string w no elemento de entrada

    • Há também uma string w separada armazenada no buffer IME

  6. O usuário digita outra letra, digamos a
  7. A string na entrada a combina com a string o buffer IME para produzir wwa .
  8. Repita os passos 1 a 7 para obter vários caracteres duplicados.

Percebi que o bug só ocorre se a entrada for renderizada novamente> 15ms após o evento compositionUpdate após a próxima repintura.

No momento, minha única solução é deixar de usar as entradas controladas.

Edit : Aqui está uma reprodução simples: https://jsfiddle.net/kbhg3xna/
Edit2 : Aqui está minha solução alternativa para hacky: https://jsfiddle.net/m792qtys/ cc: @otakustay

Alguma atualização sobre isso?

atualizar ??

alguma atualização disso?

Atordoado, fui confrontado com esta questão

Interessante, parece que o problema não é apenas sobre o onChange multi-vezes. Se não setState entre onCompositionStart e onCompositionEnd , a reação irá "controlar" o valor como está. Esta ação interromperá a composição. Isso significa que não obteremos o evento onCompositionEnd ...... (Se estiver errado, mencione-me plz.) Mas só podemos mudar o estado imediatamente (Caso contrário, teremos que enfrentar o problema @ knubie menciona). Uma reprodução aqui (parece um componente "meio controlado"): https://gist.github.com/cpdyj/6567437d96c315e9162778c8efdfb6e8

Mas estou tão surpreso que o problema não tenha solução durante cinco anos 😢

@hellendag Acho que não devemos disparar onChange até que a string IME seja confirmada.

Não acho que seja uma solução válida, pois um componente pode querer saber a string IME "não confirmada" para, por exemplo, opções de filtragem em uma lista conforme o usuário digita.

Não tenho certeza se a abordagem que uso neste outro tópico pode ajudar aqueles que estão enfrentando esse problema, mas aqui está um link para o caso: https://github.com/facebook/react/issues/13104#issuecomment -691393940

alguma atualização?

Esta página foi útil?
0 / 5 - 0 avaliações