¿Cómo activar la devolución de llamada después de actualizar el estado en Redux?

86

En React, el estado no se actualiza instantáneamente, por lo que podemos usar la devolución de llamada en setState(state, callback). ¿Pero cómo hacerlo en Redux?

Después de llamar al this.props.dispatch(updateState(key, value)), necesito hacer algo con el estado actualizado inmediatamente.

¿Hay alguna forma de llamar a una devolución de llamada con el estado más reciente como lo que hago en React?

Ladrillo Yang
fuente
¿Usas thunk?
Pranesh Ravi
1
Creo que dispatch()es una actividad sincrónica. El estado actualizado debería estar disponible en la siguiente línea.
Pranesh Ravi
3
@PraneshRavi Eso pensé. Pero obtuve el viejo estado. No usé thunk.
Brick Yang
1
Sí, el envío es sincrónico, pero la actualización posterior de los accesorios del componente no lo es. Por lo tanto, el estado redux se actualiza en la siguiente línea, pero los accesorios del componente aún no lo están.
amik

Respuestas:

112

El componente debe actualizarse para recibir nuevos accesorios.

hay formas de lograr su objetivo:

1. componentDidUpdate comprueba si el valor ha cambiado, luego haz algo ..

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

2. redux-promise (el middleware enviará el valor resuelto de la promesa)

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

luego en 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();
};

luego en componente

this.props.dispatch(updateState(key, value)).then(()=>{
   alert(this.props.value)
})
Kokovin Vladislav
fuente
6
En su redux-thunkejemplo, lo usa dispatchcomo si fuera sincrónico. ¿Es ese el caso?
Danny Harding
2
@DannyHarding dispatchno es sincrónico. Sin el Promise.resolve(), this.props.valuetodavía devolvería el valor anterior. Todavía se necesita algún tipo de aplazamiento asíncrono ( setTimeoutpor ejemplo, hacer referencia desde dentro de a también funcionaría).
Drazen Bjelovuk
4
@DrazenBjelovuk Me pregunto porque la función updateState en el ejemplo de redux-thunk tiene dispatch (someAction) seguida inmediatamente por return Promise.resolve (). La promesa se resuelve de inmediato, lo que imagino que crearía una condición de carrera entre la actualización de la tienda redux y el .entonces llamado en el componente. Debido a que el envío no devuelve una promesa ni acepta una devolución de llamada, no creo que esta sea una forma precisa de saber cuándo se ha actualizado la tienda redux. Creo que la respuesta de just-boris es correcta en este caso
Danny Harding
2
@DannyHarding Sí, estoy de acuerdo en que este método probablemente no sea una garantía segura, solo ilustra que el envío no es sincrónico.
Drazen Bjelovuk
1
Estoy tratando de usar la solución redux-thunk aquí, pero solo obtengo TypeError: _this.props.dispatch is not a function?
Krillko
14

El punto más importante de React es el flujo de datos unidireccional. En su ejemplo, eso significa que el envío de una acción y el manejo de cambios de estado deben estar desacoplados.

No debes pensar como "lo hice A, ahora se Xconvierte Yy lo manejo", sino "qué hago cuando se Xconvierte Y", sin ninguna relación con A. El estado de la tienda se puede actualizar desde varias fuentes, además de su componente, Time Travel también puede cambiar de estado y no pasará por su Apunto de envío.

Básicamente, eso significa que debe usarlo componentWillReceivePropscomo lo propuso @Utro

just-boris
fuente
Esta es la respuesta que realmente busca op (aunque parece que no es consciente de esto ..)
refaelio
1
Estoy de acuerdo, pero esto parece ser considerado un antipatrón: hackernoon.com/…
Giacomo Cerquone
1
¿Qué hacemos ahora que componentWillReceivePropsestá en desuso? static getDerivedStateFromProps()no parece un reemplazo adecuado ya que no puede llamar a una devolución de llamada, por lo que puedo decir
M3RS
7

Con API Hooks:

useEffect con el prop 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
fuente
La respuesta aceptada fue escrita antes de React Hooks. Esta debería ser la respuesta aceptada ahora después de la introducción de los ganchos. Es una forma más reactiva de manejar los cambios.
tif
5

Puede usar el subscribeoyente y se llamará cuando se envíe una acción. Dentro del oyente obtendrá los últimos datos de la tienda.

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

Pranesh Ravi
fuente
2
subscribesolo se activa cuando se envía una acción. No hay forma de subscribeque sepa si su llamada a la API ha devuelto algún dato ... ¡creo! : D
Mihir
Puede enviar otra acción cuando su solicitud se complete (con éxito o sin éxito).
Jam
No estoy convencido de que ninguna de las otras soluciones propuestas aquí realmente funcione, porque no veo la manera de que se resuelva una promesa o se active una devolución de llamada después de que se actualice el estado. Este método se llama para cada actualización de la tienda, no solo la que ocurre después de su evento, pero no debería ser demasiado difícil de solucionar. En particular, el enlace que escribe una utilidad personalizada observeStore desde la página a la que enlazó es muy útil.
Nat Kuhn
Página de enlace no encontrada
Bharat Modi
2

Puedes usar un procesador con devolución de llamada

myThunk = cb => dispatch =>
  myAsyncOp(...)
    .then(res => dispatch(res))
    .then(() => cb()) // Do whatever you want here.
    .catch(err => handleError(err))
aclamación
fuente
¡Perfecto para lo que necesitaba!
JSON C11
-1

Como solución simple, puede usar: redux-promise

Pero si usa redux-thunk , debe hacer algo como esto:

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

Mohammad P
fuente