Como você executa debounce no React.js?
Eu quero renunciar ao handleOnChange.
Eu tentei com debounce(this.handleOnChange, 200)
mas não funciona.
function debounce(fn, delay) {
var timer = null;
return function() {
var context = this,
args = arguments;
clearTimeout(timer);
timer = setTimeout(function() {
fn.apply(context, args);
}, delay);
};
}
var SearchBox = React.createClass({
render: function() {
return <input type="search" name="p" onChange={this.handleOnChange} />;
},
handleOnChange: function(event) {
// make ajax call
}
});
javascript
reactjs
Chetan Ankola
fonte
fonte
debounce
. aqui, quandoonChange={debounce(this.handleOnChange, 200)}/>
, ele será invocadodebounce function
sempre. mas, de fato, o que precisamos é chamar a função que função de retorno retornou.Respostas:
2019: experimente ganchos + prometer debouncing
Esta é a versão mais atualizada de como eu resolveria esse problema. Eu usaria:
Essa é uma fiação inicial, mas você está compondo blocos primitivos por conta própria e pode criar seu próprio gancho personalizado para que você precise fazer isso apenas uma vez.
E então você pode usar seu gancho:
Você encontrará este exemplo em execução aqui e deve ler a documentação do react-async-hook para obter mais detalhes.
2018: tente prometer renúncia
Muitas vezes, queremos rejeitar chamadas de API para evitar inundar o back-end com solicitações inúteis.
Em 2018, trabalhar com retornos de chamada (Lodash / Underscore) parece ruim e propenso a erros. É fácil encontrar problemas de clichê e simultaneidade devido à resolução de chamadas da API em uma ordem arbitrária.
Eu criei uma pequena biblioteca com o React em mente para resolver suas dores: awesome-debounce-promessa .
Isso não deve ser mais complicado do que isso:
A função debounce garante que:
this.setState({ result });
acontecerá por chamada da APIEventualmente, você pode adicionar outro truque se o seu componente desmontar:
Observe que os Observables (RxJS) também podem ser ótimos para rebater entradas, mas é uma abstração mais poderosa que pode ser mais difícil de aprender / usar corretamente.
<2017: ainda deseja usar a conversão de retorno de chamada?
A parte importante aqui é criar uma única função rejeitada (ou estrangulada) por instância do componente . Você não deseja recriar a função debounce (ou aceleração) toda vez e não deseja que várias instâncias compartilhem a mesma função debounce.
Não estou definindo uma função de depuração nesta resposta, pois ela não é realmente relevante, mas ela funcionará perfeitamente com
_.debounce
sublinhado ou lodash, além de qualquer função de depuração fornecida pelo usuário.BOA IDEIA:
Como as funções debitadas são com estado, precisamos criar uma função debitada por instância do componente .
ES6 (propriedade da classe) : recomendado
ES6 (construtor de classe)
ES5
Consulte JsFiddle : 3 instâncias estão produzindo 1 entrada de log por instância (que gera 3 globalmente).
Não é uma boa ideia:
Não funcionará, porque durante a criação do objeto de descrição da classe,
this
não é o próprio objeto criado.this.method
não retorna o que você espera, porque othis
contexto não é o objeto em si (que na verdade ainda não existe ainda, pois está sendo criado).Não é uma boa ideia:
Desta vez, você está efetivamente criando uma função rejeitada que chama seu
this.method
. O problema é que você a está recriando a cadadebouncedMethod
chamada, para que a função de rebounce recém-criada não saiba nada sobre chamadas anteriores! Você deve reutilizar a mesma função debatida ao longo do tempo ou a devolução não ocorrerá.Não é uma boa ideia:
Isso é um pouco complicado aqui.
Todas as instâncias montadas da classe compartilharão a mesma função rejeitada e, na maioria das vezes, não é isso que você deseja! Consulte JsFiddle : 3 instâncias estão produzindo apenas 1 entrada de log globalmente.
É necessário criar uma função debounce para cada instância do componente , e não uma única função debounce no nível da classe, compartilhada por cada instância do componente.
Cuide do pool de eventos do React
Isso está relacionado porque geralmente queremos rebater ou limitar os eventos do DOM.
No React, os objetos de evento (ou seja,
SyntheticEvent
) que você recebe nos retornos de chamada são agrupados (isso agora está documentado ). Isso significa que depois que o retorno de chamada do evento for chamado, o SyntheticEvent que você recebe será colocado de volta no pool com atributos vazios para reduzir a pressão do GC.Portanto, se você acessar
SyntheticEvent
propriedades de forma assíncrona com o retorno de chamada original (como pode ser o caso se você acelerar / devolver), as propriedades que você acessar poderão ser apagadas. Se você deseja que o evento nunca seja colocado de volta no pool, use opersist()
métodoSem persistir (comportamento padrão: evento em pool)
O segundo (assíncrono) será impresso
hasNativeEvent=false
porque as propriedades do evento foram limpas.Com persistir
O segundo (assíncrono) será impresso
hasNativeEvent=true
porquepersist
permite evitar colocar o evento de volta no pool.Você pode testar estes 2 comportamentos aqui: JsFiddle
Leia a resposta de Julen para obter um exemplo de uso
persist()
com uma função de aceleração / desaceleração.fonte
handleOnChange = debounce((e) => { /* onChange handler code here */ }, timeout)
no nível superior da sua classe. Você ainda está configurando efetivamente um membro da instância, mas parece um pouco mais com uma definição de método normal. Não há necessidade de umconstructor
se você ainda não tiver um definido. Suponho que seja principalmente uma preferência de estilo.componentWillUnmount
:this.method.cancel()
- caso contrário, ele pode querer definirState em um componente desmontado.Componentes não controlados
Você pode usar o
event.persist()
método .Um exemplo segue usando sublinhado
_.debounce()
:Edit: Veja este JSFiddle
Componentes controlados
Atualização: o exemplo acima mostra um componente não controlado . Eu uso elementos controlados o tempo todo, então aqui está outro exemplo acima, mas sem usar o
event.persist()
"truque".Um JSFiddle também está disponível . Exemplo sem sublinhado
Edit: exemplos atualizados e JSFiddles para reagir 0.12
Edit: exemplos atualizados para abordar a questão levantada por Sebastien Lorber
Edit: atualizado com jsfiddle que não usa sublinhado e usa rebounce javascript simples.
fonte
<input value={this.props.someprop}...
, ele não será renderizado corretamente, pois a atualização ao pressionar a tecla não voltará ao componente até depois da devolução. Não há problema em omitirvalue=
se você está feliz por isso não ser gerenciado, mas se você deseja preencher previamente o valor e / ou vinculá-lo em outro lugar, obviamente isso não funciona.persist
especialmente quando houver muitos eventos, como nomousemove
. Eu vi o código ficar totalmente sem resposta dessa maneira. É muito mais eficiente extrair os dados necessários do evento nativo na chamada de evento e, em seguida, chamar a função debounce / throttled apenas com os dados, NÃO com o próprio evento. Não há necessidade de persistir o evento que maneira2019: use o gancho de reação 'useCallback'
Depois de tentar várias abordagens diferentes, achei o uso
useCallback
o mais simples e eficiente na solução do problema de várias chamadas do usodebounce
em umonChange
evento.De acordo com a documentação da API Hooks ,
Passar uma matriz vazia como uma dependência garante que o retorno de chamada seja chamado apenas uma vez. Aqui está uma implementação simples:
Espero que isto ajude!
fonte
debounce()
considera oonChange()
retorno de chamada o mesmo método de retorno de chamada?const testFunc2 = useCallback(debounce((text) => console.log('testFunc2() has ran:', text), 1000) , []);
dentro do corpo do componente de função ou o React gera uma mensagem de erro sobre o uso do gancho fora dele. Em seguida, noonChange
manipulador de eventos:<input type='text' name='name' className='th-input-container__input' onChange={evt => {testFunc2(evt.target.value);}}
.Achei este post de Justin Tulk muito útil. Depois de algumas tentativas, da maneira que alguém perceberia ser a maneira mais oficial com o react / redux, isso mostra que falha devido ao pool de eventos sintéticos do React . Sua solução então usa algum estado interno para rastrear o valor alterado / inserido na entrada, com um retorno de chamada logo após o
setState
qual chama uma ação de redux acelerada / desacelerada que mostra alguns resultados em tempo real.fonte
Se tudo o que você precisa no objeto de evento é obter o elemento de entrada DOM, a solução é muito mais simples - basta usar
ref
. Observe que isso requer sublinhado :fonte
Depois de lutar com as entradas de texto por um tempo e sem encontrar uma solução perfeita sozinha, encontrei isso no npm: react -debounce-input .
Aqui está um exemplo simples:
O componente DebounceInput aceita todos os acessórios que você pode atribuir a um elemento de entrada normal. Experimente no codepen
Espero que ajude outra pessoa também e economize algum tempo.
fonte
Com
debounce
você, é necessário manter o evento sintético original por pertoevent.persist()
. Aqui está um exemplo de trabalho testado comReact 16+
.Com o componente funcional, você pode fazer isso -
Referências - - https://gist.github.com/elijahmanor/08fc6c8468c994c844213e4a4344a709 - https://blog.revathskumar.com/2016/02/reactjs-using-debounce-in-react-components.html
fonte
Se você estiver usando redux, poderá fazer isso de uma maneira muito elegante com o middleware. Você pode definir um
Debounce
middleware como:Em seguida, você pode adicionar a renúncia aos criadores de ações, como:
Na verdade, já existe um middleware que você pode usar para usar o npm.
fonte
applyMiddleware(...)
cadeia, se temos muitosUsando ES6 CLASS e Reagir 15.xx & lodash.debounce Im usando de Reagir refs aqui desde as perdas de eventos a este ligam internamente.
fonte
Muita informação boa aqui já, mas para ser sucinta. Isso funciona para mim ...
fonte
_debounce
invólucro, ele funciona. Eu amo essa idéia!Você pode usar o método https://lodash.com/docs/4.17.5#debounce do Lodash debounce . É simples e efetivo.
Você também pode cancelar o método de rejeição usando o método abaixo.
Espero que ajude você. Felicidades!!
fonte
Para sua informação
Aqui está outra implementação de PoC:
Espero que ajude :)
fonte
Há um
use-debounce
pacote que você pode usar com os ganchos do ReactJS.No README do pacote:
Como você pode ver no exemplo acima, ele está configurado para atualizar a variável
value
apenas uma vez a cada segundo (1000 milissegundos).fonte
Apenas mais uma variante com recente reagir e lodash.
fonte
Uma solução agradável e limpa, que não requer nenhuma dependência externa:
Debouncing com ganchos de reação
Ele usa um personalizado mais os ganchos useEffect React e o método
setTimeout
/clearTimeout
.fonte
Você tentou?
fonte
Em vez de agrupar o handleOnChange em um debounce (), por que não agrupar a chamada ajax na função de retorno de chamada dentro do debounce, não destruindo o objeto de evento. Então, algo como isto:
fonte
Aqui está um exemplo que eu inventei que envolve outra classe com um debouncer. Isso se presta muito bem a ser transformado em uma função de decorador / ordem superior:
fonte
Agora existe outra solução para o React e o React Native no final de 2019 :
react-debounce-component
É um componente, fácil de usar, pequeno e widley suportado
Exemplo:
* Eu sou o criador deste componente
fonte
Eu estava procurando uma solução para o mesmo problema e me deparei com esse segmento, além de outros, mas eles tinham o mesmo problema: se você está tentando
handleOnChange
executar uma função e precisa do valor de um destino de evento, receberácannot read property value of null
ou tal erro. No meu caso, eu também precisei preservar o contexto dathis
função debounce, pois estou executando uma ação fluível. Aqui está a minha solução, ela funciona bem para o meu caso de uso, então eu a deixo aqui caso alguém se depare com esse segmento:fonte
para
throttle
oudebounce
a melhor maneira é criar uma função de criador de modo que você pode usá-lo em qualquer lugar, por exemplo:e no seu
render
método você pode fazer:a
updateUserProfileField
método criará uma função separada sempre que você a chamar.Nota: não tente retornar o manipulador diretamente, por exemplo, isso não funcionará:
a razão pela qual isso não funcionará, pois isso gerará uma nova função do acelerador cada vez que o evento for chamado, em vez de usar a mesma função do acelerador, portanto, basicamente, o acelerador será inútil;)
Além disso, se você usa
debounce
outhrottle
não precisasetTimeout
ouclearTimeout
, é por isso que nós os usamos: Pfonte
Aqui está um trecho de código usando a abordagem do @ Abra envolvida em um componente de função (usamos tecido para a interface do usuário, basta substituí-lo por um botão simples)
fonte
Minha solução é baseada em ganchos (escritos em Typescript).
Eu tenho dois ganchos principais
useDebouncedValue
euseDebouncedCallback
Primeiro -
useDebouncedValue
Digamos que temos uma caixa de pesquisa, mas queremos solicitar ao servidor os resultados da pesquisa depois que o usuário parar de digitar 0,5s
Implementação
Segundo
useDebouncedCallback
Ele apenas cria uma função 'debounce' no escopo do seu componente.
Digamos que temos um componente com um botão que exibirá o alerta 500ms depois que você parou de clicar nele.
Implementação (note que estou usando lodash / debounce como um auxiliar)
fonte
Aqui está um exemplo de TypeScript funcional para quem usa o TS e deseja renunciar a
async
funções.fonte
um pouco tarde aqui, mas isso deve ajudar. crie essa classe (está escrita em texto datilografado, mas é fácil convertê-la em javascript)
e usar
fonte
você pode usar tlence tlence
fonte
A solução Julen é meio difícil de ler, aqui está o código de reação mais claro e direto para quem o tropeçou com base no título e não nos pequenos detalhes da pergunta.
tl; dr version : quando você atualizaria para os observadores, envie um método de agendamento para chamar e, por sua vez, notificará os observadores (ou executará ajax, etc)
Jsfiddle completo com o componente de exemplo jsfiddle
fonte
Evite usar
event.persist()
- você deseja permitir que o React recicle o evento sintético. Eu acho que a maneira mais limpa de usar classes ou ganchos é dividir o retorno de chamada em duas partes:Aulas
Funções
Observe que, se sua
handleMouseOver
função usa o estado de dentro do componente, você deve usá-lo emuseMemo
vez deuseRef
e passá-lo como dependências, caso contrário você estará trabalhando com dados obsoletos (não se aplica às classes, é claro).fonte
Gancho de extensão
Use gancho
https://trippingoncode.com/react-debounce-hook/
fonte
Conheci esse problema hoje. Resolvido usando
setTimeout
eclearTimeout
.Vou dar um exemplo que você pode adaptar:
Você também pode refatorá-lo em seu próprio componente de função:
E use-o como:
fonte