Como ativar o callback após atualizar o estado no Redux?

86

No React, o estado não é atualizado instantaneamente, portanto, podemos usar o retorno de chamada em setState(state, callback). Mas como fazer no Redux?

Depois de chamar o this.props.dispatch(updateState(key, value)), preciso fazer algo com o estado atualizado imediatamente.

Existe alguma maneira de chamar um retorno de chamada com o estado mais recente, como o que faço no React?

Brick Yang
fonte
Você usa thunk?
Pranesh Ravi
1
Eu acho que dispatch()é uma atividade síncrona. O estado atualizado deve estar disponível na próxima linha.
Pranesh Ravi de
3
@PraneshRavi Achei que sim. Mas eu tenho o antigo estado. Eu não usei thunk.
Brick Yang,
1
Sim, o despacho é síncrono, mas a atualização subsequente das propriedades do componente não. Portanto, o estado redux é atualizado na próxima linha, mas os adereços do componente ainda não.
amik

Respostas:

112

componente deve ser atualizado para receber novos adereços.

existem maneiras de atingir seu objetivo:

1. componentDidUpdate verifica se o valor foi alterado, então faça algo ..

 componentDidUpdate(prevProps){
     if(prevProps.value !== this.props.value){ alert(prevProps.value) }
  }

2. redux-promessa (middleware irá despachar o valor resolvido da promessa)

export const updateState = (key, value)=>
Promise.resolve({
  type:'UPDATE_STATE',
  key, value
})

então em componente

this.props.dispatch(updateState(key, value)).then(()=>{
   alert(this.props.value)
})

2. redux-thunk

export const updateState = (key, value) => dispatch => {
  dispatch({
    type: 'UPDATE_STATE',
    key,
    value,
  });
  return Promise.resolve();
};

então em componente

this.props.dispatch(updateState(key, value)).then(()=>{
   alert(this.props.value)
})
Kokovin Vladislav
fonte
6
No seu redux-thunkexemplo, você usa dispatchcomo se fosse síncrono. É esse o caso?
Danny Harding
2
@DannyHarding dispatchnão é síncrono. Sem o Promise.resolve(), this.props.valueainda retornaria o valor antigo. Algum tipo de adiamento assíncrono ainda é necessário (referenciar de dentro de um setTimeouttambém funcionaria, por exemplo).
Drazen Bjelovuk
4
@DrazenBjelovuk Estou pensando porque a função updateState no exemplo redux-thunk tem dispatch (someAction) imediatamente seguido por return Promise.resolve (). A promessa é resolvida imediatamente, o que eu imagino que criaria uma condição de corrida entre a atualização da loja redux e o. Então chamado no componente. Como o dispatch não retorna uma promessa ou aceita um retorno de chamada, não acho que esta seja uma maneira precisa de saber quando o redux store foi atualizado. Acho que a resposta de apenas-boris está correta neste caso
Danny Harding
2
@DannyHarding Sim, eu concordo que esse método provavelmente não é uma garantia certa de fogo, apenas ilustrando que o despacho não é síncrono.
Drazen Bjelovuk
1
Estou tentando usar a solução redux-thunk aqui, mas só consigo TypeError: _this.props.dispatch is not a function?
Krillko
14

O ponto mais importante sobre React é o fluxo de dados unilateral. Em seu exemplo, isso significa que o despacho de uma ação e o tratamento de mudança de estado devem ser separados.

Você não deve pensar como "eu fiz A, agora Xvira Ye eu cuido", mas "o que faço quando Xse torna Y", sem qualquer relação com A. O estado da loja pode ser atualizado a partir de várias fontes, além de seu componente, a viagem no tempo também pode mudar de estado e não será passada por seu Aponto de despacho.

Basicamente, isso significa que você deve usar componentWillReceivePropscomo foi proposto por @Utro

apenas-boris
fonte
Esta é a resposta que op está realmente procurando (embora pareça que ele não está ciente disso ..)
refaelio
1
Eu concordo, mas isso parece ser considerado um antipadrão: hackernoon.com/…
Giacomo Cerquone
1
o que fazemos agora que componentWillReceivePropsestá obsoleto? static getDerivedStateFromProps()não parece um substituto adequado, pois não pode chamar um retorno de chamada, pelo que eu posso dizer
M3RS
7

Com API Hooks:

useEffect com o suporte como entrada.

import React, { useEffect} from 'react'
import { useSelector } from 'react-redux'

export default function ValueComponent() {
   const value = useSelectror(store => store.pathTo.value)

   useEffect(() => {
     console.log('New value', value) 
     return () => {
        console.log('Prev value', value) 
     }

   }, [value])

   return <div> {value} </div>
}
YairTawil
fonte
A resposta aceita foi escrita antes do React Hooks. Esta deve ser a resposta aceita agora, após a introdução dos ganchos. É uma maneira mais reativa de lidar com as mudanças.
tif
5

Você pode usar o subscribelistener e ele será chamado quando uma ação for despachada. Dentro do ouvinte, você obterá os dados da loja mais recentes.

http://redux.js.org/docs/api/Store.html#subscribelistener

Pranesh Ravi
fonte
2
subscribesó dispara quando uma ação é despachada. Não há como subscribeinformá-lo se sua chamada de API retornou algum dado .. eu acho! : D
Mihir
Você pode enviar outra ação quando sua solicitação for concluída (com ou sem sucesso).
Jam
Não estou convencido de que qualquer uma das outras soluções propostas aqui realmente funcione, porque não vejo uma maneira de uma promessa ser resolvida ou um retorno de chamada para disparar depois que o estado for atualizado. Este método é chamado para cada atualização da loja, não apenas aquela que acontece após o seu evento, mas não deve ser muito difícil de contornar. Em particular, o link para escrever um utilitário observeStore personalizado a partir da página vinculada é muito útil.
Nat Kuhn de
Página de link não encontrada
Bharat Modi
2

Você pode usar uma conversão com um retorno de chamada

myThunk = cb => dispatch =>
  myAsyncOp(...)
    .then(res => dispatch(res))
    .then(() => cb()) // Do whatever you want here.
    .catch(err => handleError(err))
acmoune
fonte
Perfeito para o que eu estava precisando!
JSON C11
-1

Como uma solução simples, você pode usar: redux-promessa

Mas se você estiver usando redux-thunk , você deve fazer o sth assim:

function addCost(data) {
  return dispatch => {
    var promise1 = new Promise(function(resolve, reject) {
      dispatch(something);
     });
    return promise1;
  }
}

Mohammad P
fonte