debouncing correto de uma chamada onChange to update usando react-monaco-editor

Oct 18 2020

EDITAR:

esta questão resume meu problema muito bem: o editor monaco pega o valor atual do estado, enquanto debounce impede que o estado seja disparado. Como posso permitir que um usuário continue digitando sem interrupções, sem fazer chamadas desnecessárias para atualizar o código.

----- END EDIT -----

ok, então eu tenho um app react (sem CRA) que usa o react-monaco-editor como um painel de editor de código. quando um usuário digita, o evento onChange é disparado e eu gostaria de chamar uma ação de atualização depurada, mas isso não está funcionando agora.

A configuração não é exatamente direta, mas eu tenho um componente pai, CodeEditorPanel, que tem um componente filho, monacoEditor. Eu removi o máximo possível de código que não está diretamente relacionado à questão, mas posso adicionar mais detalhes, se necessário.

usar updateCode com modo automático (UPDATE_METHODS.AUTOMATIC.value) funciona bem, mas é computacionalmente caro, mas quando eu uso o modo debelado (UPDATE_METHODS.DEBOUNCE.value), as alterações são bagunçadas e parecem estar usando valores antigos (parece o que aconteceria se eu não estivesse usando useRef, mas estou.). De qualquer forma, algo (não é assim) obviamente não está certo e eu agradeceria qualquer ajuda para detectar meu (que espero, pequeno) erro.

// CodeEditorPanel.js
import React, { useRef } from 'react';

const CodeEditorPanel = ({
  updateMethod, // editor setting, user can choose: automatic, manual, throttled, debounced
  updateCode, // the update action I want to debounce
}) => {

  const debouncedUpdate = useRef(debounce(updateCode, debounceInterval)).current;

  const getUpdateMethod = () => {
    switch (updateMethod) {
      case UPDATE_METHODS.DEBOUNCE.value:
        return debouncedUpdate;
      case UPDATE_METHODS.THROTTLE.value:
        return throttledUpdate;
      case UPDATE_METHODS.MANUAL.value:
        return ({ code, selectedFile }) => updateCode({ code, selectedFile, buffer: true });
      case UPDATE_METHODS.AUTOMATIC.value:
      default:
        return updateCode;
    }
  };

  // in reality there is a function call before update getUpdateMethod but I'm trying to simplify
  return <MonacoEditor updateCode={getUpdateMethod()} />;

};

// 
class monacoEditor extends Component {

  onChange(newValue, e) {
    const { selectedFile, updateCode } = this.props;

    // want to debounce this bad boy right here
    updateCode({ code: newValue, selectedFile });
  }

  render() {
    <MonacoEditor
      ref="monaco"
      language={language}
      theme="vs-dark"
      defaultValue=""
      value={selectedFile.content}
      options={MONACO_DEFAULT_OPTIONS}
      onChange={this.onChange}
      editorWillMount={this.editorWillMount}
      editorDidMount={this.editorDidMount}
    />
  }

}

Respostas

1 cbutler Oct 19 2020 at 01:31

Eu encontrei uma solução que funciona para mim, mas ainda estou interessado se outras pessoas tiverem ideias.

Então o que eu fiz foi:

  1. criar uma variável de estado local em meu componente e inicializar para o código prop.
  2. no método onChange, em vez de chamar o criador da ação, atualizo o estado local.
  3. Eu adicionei um cronômetro de inatividade ao componente definido para um segundo. Portanto, se o usuário parar de digitar por um segundo, ele chamará o criador da ação para atualizar o redux.
MikeLischke Oct 19 2020 at 13:57

Minha abordagem é dupla:

  1. Se uma ação imediata for necessária (por exemplo, autocompletar código), então eu executo a tarefa em cada mudança.

  2. Se uma ação puder ser atrasada (por exemplo, verificação de erro), a mudança real não importa. Nesse caso, eu inicio um cronômetro (que é zerado a cada alteração) e executo as ações necessárias, uma vez que dispara. Não há necessidade de manter o estado neste caso, uma vez que não é necessário.

Claro, se a mudança real ainda importa (digamos, para limitar uma ação a um determinado intervalo que foi modificado desde a última execução), então você obviamente precisaria armazená-la (ou um valor derivado, como um intervalo).