Eu tenho uma ação que atualiza o estado de notificação do meu aplicativo. Normalmente, esta notificação será um erro ou informação de algum tipo. Preciso despachar outra ação após 5 segundos que retornará o estado de notificação ao inicial, portanto, nenhuma notificação. A principal razão por trás disso é fornecer funcionalidade em que as notificações desaparecem automaticamente após 5 segundos.
Não tive sorte em usar setTimeout
e retornar outra ação e não consigo descobrir como isso é feito online. Portanto, qualquer conselho é bem-vindo.
redux-saga
resposta com base se você quiser algo melhor do que thunks. Resposta tardia, para que você tenha que rolar muito tempo antes de vê-la aparecer :) não significa que não vale a pena ler. Aqui está um atalho: stackoverflow.com/a/38574266/82609 #Respostas:
Não caia na armadilha de pensar que uma biblioteca deve prescrever como fazer tudo . Se você quiser fazer algo com um tempo limite em JavaScript, precisará usar
setTimeout
. Não há razão para que as ações do Redux sejam diferentes.Redux não oferecer algumas formas alternativas de lidar com coisas assíncrona, mas você só deve usar esses quando você percebe que você está repetindo muito código. A menos que você tenha esse problema, use o que o idioma oferece e procure a solução mais simples.
Escrevendo código assíncrono em linha
Esta é de longe a maneira mais simples. E não há nada específico para o Redux aqui.
Da mesma forma, de dentro de um componente conectado:
A única diferença é que, em um componente conectado, você geralmente não tem acesso à própria loja, mas obtém um
dispatch()
ou mais criadores de ações específicas injetados como acessórios. No entanto, isso não faz nenhuma diferença para nós.Se você não gosta de digitar erros ao despachar as mesmas ações de componentes diferentes, convém extrair criadores de ação em vez de despachar objetos de ação em linha:
Ou, se você os vinculou anteriormente a
connect()
:Até agora, não usamos nenhum middleware ou outro conceito avançado.
Extraindo criador de ação assíncrona
A abordagem acima funciona bem em casos simples, mas você pode achar que tem alguns problemas:
HIDE_NOTIFICATION
, ocultando erroneamente a segunda notificação mais cedo do que após o tempo limite.Para resolver esses problemas, você precisaria extrair uma função que centralize a lógica de tempo limite e despache essas duas ações. Pode ser assim:
Agora os componentes podem usar
showNotificationWithTimeout
sem duplicar essa lógica ou ter condições de corrida com notificações diferentes:Por que
showNotificationWithTimeout()
aceitadispatch
como o primeiro argumento? Porque ele precisa despachar ações para a loja. Normalmente, um componente tem acesso,dispatch
mas, como queremos que uma função externa assuma o controle sobre o envio, precisamos fornecer controle sobre o envio.Se você exportou uma loja singleton de algum módulo, basta importá-la e
dispatch
diretamente nela:Parece mais simples, mas não recomendamos essa abordagem . A principal razão pela qual não gostamos é porque força a loja a ser um singleton . Isso dificulta muito a implementação renderização do servidor . No servidor, você deseja que cada solicitação tenha seu próprio armazenamento, para que usuários diferentes obtenham diferentes dados pré-carregados.
Uma loja singleton também torna os testes mais difíceis. Você não pode mais zombar de uma loja ao testar criadores de ação, porque eles fazem referência a uma loja real específica exportada de um módulo específico. Você não pode nem redefinir seu estado de fora.
Portanto, embora você possa exportar tecnicamente uma loja singleton de um módulo, nós a desencorajamos. Não faça isso, a menos que tenha certeza de que seu aplicativo nunca adicionará renderização de servidor.
Voltando à versão anterior:
Isso resolve os problemas com a duplicação da lógica e nos salva das condições da corrida.
Thunk Middleware
Para aplicativos simples, a abordagem deve ser suficiente. Não se preocupe com o middleware, se você estiver feliz com isso.
Em aplicativos maiores, no entanto, você pode encontrar alguns inconvenientes.
Por exemplo, parece lamentável que tenhamos que repassar
dispatch
. Isso torna mais difícil separar componentes de contêiner e de apresentação, pois qualquer componente que despacha ações de Redux de forma assíncrona da maneira acima deve ser aceitodispatch
como um suporte para que possa passar adiante. Você não pode mais vincular os criadores de açãoconnect()
porqueshowNotificationWithTimeout()
não é realmente um criador de ação. Não retorna uma ação Redux.Além disso, pode ser estranho lembrar quais funções são criadoras de ação síncronas
showNotification()
e quais são ajudantes assíncronas, comoshowNotificationWithTimeout()
. Você precisa usá-los de maneira diferente e tome cuidado para não confundi-los.Essa foi a motivação para encontrar uma maneira de "legitimar" esse padrão de fornecer
dispatch
a uma função auxiliar e ajudar o Redux a "ver" tais criadores de ação assíncronos como um caso especial de criadores de ação normal, em vez de funções totalmente diferentes.Se você ainda está conosco e também o reconhece como um problema no seu aplicativo, pode usar o middleware Redux Thunk .
Em essência, o Redux Thunk ensina o Redux a reconhecer tipos especiais de ações que de fato funcionam:
Quando esse middleware está ativado, se você despacha uma função , o middleware Redux Thunk fornece
dispatch
como argumento. Ele também “engolirá” essas ações, portanto, não se preocupe com seus redutores recebendo argumentos de funções estranhos. Seus redutores receberão apenas ações simples de objetos - emitidas diretamente ou emitidas pelas funções, como acabamos de descrever.Isso não parece muito útil, não é? Não nesta situação em particular. No entanto, permite-nos declarar
showNotificationWithTimeout()
como um criador de ação Redux comum:Observe como a função é quase idêntica à que escrevemos na seção anterior. No entanto, ele não aceita
dispatch
como o primeiro argumento. Em vez disso, ele retorna uma função que aceitadispatch
como o primeiro argumento.Como o usaríamos em nosso componente? Definitivamente, poderíamos escrever isso:
Estamos chamando o criador de ação assíncrona para obter a função interna que deseja apenas
dispatch
e depois passamosdispatch
.No entanto, isso é ainda mais complicado do que a versão original! Por que nós seguimos esse caminho?
Por causa do que eu te disse antes. Se o middleware Redux Thunk estiver ativado, sempre que você tentar despachar uma função em vez de um objeto de ação, o middleware chamará essa função com o
dispatch
próprio método como o primeiro argumento .Então, podemos fazer isso:
Por fim, despachar uma ação assíncrona (na verdade, uma série de ações) não parece diferente de despachar uma única ação de forma síncrona para o componente. O que é bom porque os componentes não devem se importar se algo acontece de forma síncrona ou assíncrona. Nós apenas abstraímos isso.
Observe que, uma vez que "ensinamos" o Redux a reconhecer esses criadores de ação "especiais" (os chamamos de criadores de thunk action), agora podemos usá-los em qualquer lugar em que usaríamos criadores de ação regulares. Por exemplo, podemos usá-los com
connect()
:Estado da leitura em Thunks
Geralmente, seus redutores contêm a lógica de negócios para determinar o próximo estado. No entanto, os redutores somente entram em ação após o envio das ações. E se você tiver um efeito colateral (como chamar uma API) em um criador de ações de thunk e quiser evitá-lo sob alguma condição?
Sem usar o middleware thunk, basta fazer esta verificação dentro do componente:
No entanto, o objetivo de extrair um criador de ações era centralizar essa lógica repetitiva em muitos componentes. Felizmente, o Redux Thunk oferece uma maneira de ler o estado atual da loja Redux. Além disso
dispatch
, ele também passagetState
como o segundo argumento para a função que você retorna do seu criador de ações de thunk. Isso permite que o thunk leia o estado atual da loja.Não abuse deste padrão. É bom para resgatar chamadas de API quando há dados em cache disponíveis, mas não é uma base muito boa para construir a lógica de negócios. Se você usar
getState()
apenas para despachar condicionalmente ações diferentes, considere colocar a lógica de negócios nos redutores.Próximos passos
Agora que você tem uma intuição básica sobre como os thunks funcionam, confira o exemplo assíncrono do Redux que os utiliza.
Você pode encontrar muitos exemplos nos quais os thunks retornam promessas. Isso não é necessário, mas pode ser muito conveniente. O Redux não se importa com o que você retorna de um thunk, mas fornece seu valor de retorno
dispatch()
. É por isso que você pode retornar uma promessa a partir de uma conversão e aguardar a conclusão, ligandodispatch(someThunkReturningPromise()).then(...)
.Você também pode dividir criadores de ação thunk complexos em vários criadores de ação thunk menores. O
dispatch
método fornecido pelos thunks pode aceitar os próprios thunks, para que você possa aplicar o padrão recursivamente. Novamente, isso funciona melhor com o Promises, porque você pode implementar o fluxo de controle assíncrono.Para alguns aplicativos, você pode se deparar com uma situação em que seus requisitos de fluxo de controle assíncrono são muito complexos para serem expressos com thunks. Por exemplo, tentar novamente solicitações com falha, fluxo de nova autorização com tokens ou uma integração passo a passo pode ser muito detalhado e propenso a erros quando escrito dessa maneira. Nesse caso, convém procurar soluções de fluxo de controle assíncrono mais avançadas, como Redux Saga ou Redux Loop . Avalie-os, compare os exemplos relevantes para suas necessidades e escolha o que você mais gosta.
Finalmente, não use nada (incluindo thunks) se você não tiver a necessidade genuína deles. Lembre-se de que, dependendo dos requisitos, sua solução pode parecer tão simples quanto
Não se preocupe, a menos que você saiba por que está fazendo isso.
fonte
if (cond) dispatch({ type: 'A' }) else dispatch({ type: 'B' })
talvez deva apenasdispatch({ type: 'C', something: cond })
optar por ignorar a ação nos redutores, dependendoaction.something
do estado atual.Usando Redux-saga
Como disse Dan Abramov, se você quiser um controle mais avançado sobre seu código assíncrono, poderá dar uma olhada na redux-saga .
Esta resposta é um exemplo simples. Se você deseja melhores explicações sobre por que o redux-saga pode ser útil para a sua aplicação, verifique esta outra resposta .
A idéia geral é que o Redux-saga ofereça um interpretador de geradores ES6 que permita que você escreva facilmente códigos assíncronos que se assemelhem a códigos síncronos (é por isso que você encontrará muitas vezes infinitas loops no Redux-saga). De alguma forma, o Redux-saga está construindo sua própria linguagem diretamente dentro do Javascript. O Redux-saga pode parecer um pouco difícil de aprender no começo, porque você precisa de conhecimentos básicos sobre geradores, mas também entende o idioma oferecido pelo Redux-saga.
Vou tentar aqui descrever aqui o sistema de notificação que construí em cima da redux-saga. Este exemplo atualmente é executado em produção.
Especificação avançada do sistema de notificação
Resultado
Captura de tela do meu aplicativo de produção Stample.co
Código
Aqui eu chamei a notificação de
toast
mas este é um detalhe de nomeação.E o redutor:
Uso
Você pode simplesmente despachar
TOAST_DISPLAY_REQUESTED
eventos. Se você enviar quatro solicitações, apenas três notificações serão exibidas e a quarta aparecerá um pouco mais tarde quando a 1ª notificação desaparecer.Observe que eu não recomendo especificamente o envio
TOAST_DISPLAY_REQUESTED
pelo JSX. Você prefere adicionar outra saga que ouça seus eventos de aplicativos já existentes e, em seguida, despache oTOAST_DISPLAY_REQUESTED
: seu componente que aciona a notificação, não precisa estar fortemente acoplado ao sistema de notificação.Conclusão
Meu código não é perfeito, mas é executado em produção com 0 bugs por meses. Redux-saga e geradores são um pouco difíceis inicialmente, mas depois que você os entende, esse tipo de sistema é muito fácil de construir.
É muito fácil implementar regras mais complexas, como:
Honestamente, boa sorte em implementar esse tipo de coisa corretamente com thunks.
Note que você pode fazer exatamente o mesmo tipo de coisa com redux-observable, que é muito semelhante à redux-saga. É quase o mesmo e é uma questão de gosto entre geradores e RxJS.
fonte
yield call(delay,timeoutValue);
: não é a mesma API, mas tem o mesmo efeitoUm repositório com projetos de amostra
Atualmente, existem quatro projetos de amostra:
A resposta aceita é incrível.
Mas há algo faltando:
Então, eu criei o repositório Hello Async para adicionar as coisas ausentes:
Redux Saga
A resposta aceita já fornece trechos de código de amostra para Código assíncrono em linha, Gerador de ação assíncrona e Redux Thunk. Por uma questão de integridade, forneço trechos de código para o Redux Saga:
Ações são simples e puras.
Nada é especial com o componente.
Sagas são baseadas em geradores ES6
Comparado ao Redux Thunk
Prós
Contras
Consulte o projeto executável se os trechos de código acima não responderem a todas as suas perguntas.
fonte
Você pode fazer isso com redux-thunk . Há um guia no documento redux para ações assíncronas como setTimeout.
fonte
applyMiddleware(ReduxPromise, thunk)(createStore)
é assim que você adiciona vários middleware (coma separados?), Pois eu não consigo fazer o thunk funcionar.const store = createStore(reducer, applyMiddleware([ReduxPromise, thunk]));
Eu recomendaria também dar uma olhada no padrão SAM .
O padrão SAM defende a inclusão de um "predicado da próxima ação", em que ações (automáticas) como "as notificações desaparecem automaticamente após 5 segundos" são acionadas após a atualização do modelo (modelo do SAM ~ estado redutor + armazenamento).
O padrão defende ações de seqüenciamento e mutações de modelo uma por vez, porque o "estado de controle" do modelo "controla" quais ações são ativadas e / ou executadas automaticamente pelo predicado da próxima ação. Você simplesmente não pode prever (em geral) qual o estado do sistema antes do processamento de uma ação e, portanto, se sua próxima ação esperada será permitida / possível.
Por exemplo, o código,
não seria permitido com o SAM, porque o fato de uma ação hideNotification poder ser despachada depende do modelo que aceita com êxito o valor "showNotication: true". Pode haver outras partes do modelo que o impedem de aceitá-lo e, portanto, não há razão para acionar a ação hideNotification.
Eu recomendo que implemente um predicado de próxima ação adequado após a atualização das lojas e o novo estado de controle do modelo. Essa é a maneira mais segura de implementar o comportamento que você está procurando.
Você pode se juntar a nós no Gitter, se quiser. Há também um guia de introdução ao SAM disponível aqui .
fonte
V = S( vm( M.present( A(data) ) ), nap(M))
é simplesmente lindo. Obrigado por compartilhar seus pensamentos e experiências. Eu vou cavar mais fundo.Depois de experimentar as várias abordagens populares (criadores de ação, thunks, sagas, épicos, efeitos, middleware personalizado), eu ainda sentia que talvez houvesse espaço para melhorias, então documentei minha jornada neste artigo do blog, Onde coloco minha lógica de negócios? um aplicativo React / Redux?
Assim como as discussões aqui, tentei contrastar e comparar as várias abordagens. Eventualmente, isso me levou a apresentar uma nova biblioteca redux-logic, que se inspira em épicos, sagas e middleware personalizado.
Ele permite interceptar ações para validar, verificar, autorizar e fornecer uma maneira de executar E / S assíncronas.
Algumas funcionalidades comuns podem ser simplesmente declaradas como devolução, limitação, cancelamento e apenas o uso da resposta da solicitação mais recente (takeLatest). A redux-logic envolve seu código, fornecendo essa funcionalidade para você.
Isso libera você para implementar sua lógica de negócios principal da maneira que desejar. Você não precisa usar observáveis ou geradores, a menos que queira. Use funções e retornos de chamada, promessas, funções assíncronas (async / waitit), etc.
O código para fazer uma notificação 5s simples seria algo como:
Eu tenho um exemplo de notificação mais avançado no meu repositório que funciona de maneira semelhante ao que Sebastian Lorber descreveu onde você pode limitar a exibição a N itens e alternar entre os que estão na fila. exemplo de notificação redux-logic
Eu tenho uma variedade de exemplos ao vivo do redux-logic jsfiddle, bem como exemplos completos . Continuo trabalhando em documentos e exemplos.
Eu adoraria ouvir seus comentários.
fonte
Entendo que essa pergunta é um pouco antiga, mas vou apresentar outra solução usando o redux-observable aka. Épico.
Citando a documentação oficial:
O que é observável no redux?
Um épico é o primitivo central do redux observável.
Em mais ou menos palavras, você pode criar uma função que receba ações por meio de um fluxo e, em seguida, retornar um novo fluxo de ações (usando efeitos colaterais comuns, como tempos limite, atrasos, intervalos e solicitações).
Deixe-me postar o código e depois explicar um pouco mais sobre ele
store.js
index.js
App.js
O código-chave para resolver esse problema é tão fácil quanto você pode ver, a única coisa que parece diferente das outras respostas é a função rootEpic.
Ponto 1. Assim como as sagas, você precisa combinar os épicos para obter uma função de nível superior que receba um fluxo de ações e retorne um fluxo de ações, para poder usá-lo com o createEpicMiddleware da fábrica de middleware . No nosso caso, precisamos apenas de um, portanto, temos apenas o nosso rootEpic, para que não tenhamos que combinar nada, mas é um fato bom saber.
Ponto 2. Nosso rootEpic, que cuida da lógica dos efeitos colaterais, leva apenas 5 linhas de código, o que é incrível! Incluindo o fato de que é praticamente declarativo!
Ponto 3. Explicação rootEpic linha a linha (nos comentários)
Espero que ajude!
fonte
switchMap
?Por que deveria ser tão difícil? É apenas lógica da interface do usuário. Use uma ação dedicada para definir dados de notificação:
e um componente dedicado para exibi-lo:
Nesse caso, as perguntas devem ser "como você limpa o estado antigo?", "Como notificar um componente que o tempo mudou"
Você pode implementar alguma ação TIMEOUT que é despachada em setTimeout a partir de um componente.
Talvez seja bom limpá-lo sempre que uma nova notificação for exibida.
Enfim, deve haver algum
setTimeout
lugar, certo? Por que não fazer isso em um componenteA motivação é que a funcionalidade "notificação desaparece" é realmente uma preocupação da interface do usuário. Por isso, simplifica os testes para sua lógica de negócios.
Não parece fazer sentido testar como é implementado. Faz sentido verificar quando a notificação deve expirar. Assim, menos código para stub, testes mais rápidos, código mais limpo.
fonte
Se você deseja controlar o tempo limite de ações seletivas, tente a abordagem do middleware . Eu enfrentei um problema semelhante ao lidar com ações baseadas em promessas seletivamente e essa solução era mais flexível.
Digamos que seu criador de ações seja assim:
o tempo limite pode conter vários valores na ação acima
Sua implementação de middleware ficaria assim:
Agora você pode rotear todas as suas ações por essa camada de middleware usando o redux.
Você pode encontrar alguns exemplos semelhantes aqui
fonte
A maneira apropriada de fazer isso é usando o Redux Thunk, que é um middleware popular para o Redux, conforme a documentação do Redux Thunk:
Então, basicamente, ele retorna uma função e você pode atrasar seu envio ou colocá-lo em um estado de condição.
Então, algo assim fará o trabalho para você:
fonte
É simples Use o pacote trim-redux e escreva assim
componentDidMount
ou em outro lugar e mate-ocomponentWillUnmount
.fonte
O Redux em si é uma biblioteca bastante detalhada e, para essas coisas, você teria que usar algo como Redux-thunk , que dará uma
dispatch
função, para que você possa despachar o fechamento da notificação após alguns segundos.Eu criei uma biblioteca para resolver problemas como verbosidade e composição, e seu exemplo será parecido com o seguinte:
Assim, compomos ações de sincronização para mostrar notificações dentro da ação assíncrona, que podem solicitar algumas informações em segundo plano ou verificar mais tarde se a notificação foi fechada manualmente.
fonte