Jak wyłączyć callback po zaktualizowaniu stanu w Redux?

86

W Reakcie stan nie jest aktualizowany od razu, więc możemy użyć wywołania zwrotnego w setState(state, callback). Ale jak to zrobić w Redux?

Po wywołaniu this.props.dispatch(updateState(key, value))muszę natychmiast coś zrobić ze zaktualizowanym stanem.

Czy jest jakiś sposób, żebym mógł wywołać oddzwonienie z najnowszym stanem, takim jak to, co robię w React?

Brick Yang
źródło
Czy używasz thunk?
Pranesh Ravi
1
Myślę, że dispatch()jest to działanie synchroniczne. Zaktualizowany stan powinien być dostępny w następnej linii.
Pranesh Ravi
3
@PraneshRavi Tak myślałem. Ale mam stary stan. Nie użyłem thunk.
Brick Yang
1
Tak, wysyłka jest synchroniczna, ale późniejsza aktualizacja właściwości komponentu nie. Dlatego stan redux jest aktualizowany w następnej linii, ale właściwości składnika jeszcze nie.
amik

Odpowiedzi:

112

komponent powinien zostać zaktualizowany, aby otrzymać nowe rekwizyty.

są sposoby na osiągnięcie celu:

1. componentDidUpdate sprawdź, czy wartość została zmieniona, a następnie zrób coś ..

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

2. obietnica redux (oprogramowanie pośredniczące prześle wartość obietnicy)

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

następnie w komponencie

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

następnie w komponencie

this.props.dispatch(updateState(key, value)).then(()=>{
   alert(this.props.value)
})
Kokovin Vladislav
źródło
6
W twoim redux-thunkprzykładzie używasz dispatchtak, jakby był synchroniczny. Czy tak jest?
Danny Harding
2
@DannyHarding dispatchnie jest synchroniczne. Bez Promise.resolve(), this.props.valuenadal zwróci starą wartość. Nadal potrzebne jest pewnego rodzaju odroczenie asynchronizacji (na przykład odniesienie z wewnątrz setTimeoutmoże również działać).
Drazen Bjelovuk
4
@DrazenBjelovuk Zastanawiam się, ponieważ funkcja updateState w przykładzie redux-thunk ma dispatch (someAction), po którym natychmiast następuje return Promise.resolve (). Obietnica zostaje rozwiązana natychmiast, co, jak wyobrażam, stworzyłoby sytuację wyścigu między aktualizacją sklepu redux a wywołaniem komponentu. Ponieważ wysyłka nie zwraca obietnicy ani nie przyjmuje oddzwonienia, nie sądzę, że jest to właściwy sposób sprawdzenia, kiedy sklep Redux został zaktualizowany. Myślę, że odpowiedź just-borisa jest w tym przypadku poprawna
Danny Harding
2
@DannyHarding Yeah Zgadzam się, że ta metoda prawdopodobnie nie zapewnia pewnej gwarancji pożaru, ilustrując tylko, że wysyłka nie jest synchroniczna.
Drazen Bjelovuk
1
Próbuję tutaj użyć rozwiązania redux-thunk, ale dostaję tylko TypeError: _this.props.dispatch is not a function?
Krillko
14

Najważniejszą kwestią dotyczącą Reacta jest jednokierunkowy przepływ danych. W twoim przykładzie oznacza to, że wysyłanie akcji i obsługa zmiany stanu powinny być rozdzielone.

Nie powinieneś myśleć w stylu „Zrobiłem A, teraz się Xstaje Yi sobie z tym poradzę”, ale „Co mam zrobić, kiedy Xsię stanie Y”, bez żadnego związku A. Stan sklepu może być aktualizowany z wielu źródeł, oprócz komponentu, Podróże w czasie mogą również zmieniać stan i nie będą przekazywane przez Twój Apunkt wysyłkowy.

Zasadniczo oznacza to, że powinieneś używać tego, componentWillReceivePropsco zostało zaproponowane przez @Utro

Just-Boris
źródło
To jest odpowiedź, której op naprawdę szuka (chociaż wygląda na to, że nie jest tego świadomy ..)
refaelio
1
Zgadzam się, ale to wydaje się być uważane za anty-wzór: hackernoon.com/…
Giacomo Cerquone
1
co teraz robimy, co componentWillReceivePropsjest przestarzałe? static getDerivedStateFromProps()nie wydaje się odpowiednim zamiennikiem, ponieważ nie może oddzwonić, o ile wiem
M3RS
7

Z Hooks API:

useEffect z prop jako danymi wejściowymi.

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
źródło
Zaakceptowana odpowiedź została napisana przed React Hooks. To powinna być akceptowana odpowiedź teraz po wprowadzeniu haków. To bardziej reaktywny sposób radzenia sobie ze zmianami.
tif
5

Możesz użyć subscribe listenera, a zostanie on wywołany, gdy zostanie uruchomiona akcja. Wewnątrz odbiornika znajdziesz najnowsze dane sklepu.

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

Pranesh Ravi
źródło
2
subscribestrzela tylko wtedy, gdy akcja jest wysyłana. Nie ma sposobu subscribe, abyś wiedział, czy twoje wywołanie API zwróciło jakieś dane ... myślę! : D
Mihir
Możesz wysłać inną akcję, gdy Twoje żądanie zostanie zakończone (pomyślnie lub bez powodzenia).
Jam
Nie jestem przekonany, że którekolwiek z innych proponowanych tutaj rozwiązań faktycznie działa, ponieważ nie widzę sposobu na rozwiązanie obietnicy lub oddzwonienie do ognia po aktualizacji stanu. Ta metoda jest wywoływana przy każdej aktualizacji sklepu, a nie tylko tej, która ma miejsce po Twoim zdarzeniu, ale obejście tego nie powinno być zbyt trudne. W szczególności bardzo pomocny jest link do tworzenia niestandardowego narzędzia obserwatorStore ze strony, do której prowadziło łącze .
Nat Kuhn
Nie znaleziono strony z odnośnikiem
Bharat Modi
2

Możesz użyć thunk z oddzwonieniem

myThunk = cb => dispatch =>
  myAsyncOp(...)
    .then(res => dispatch(res))
    .then(() => cb()) // Do whatever you want here.
    .catch(err => handleError(err))
acmoune
źródło
Idealne do tego, czego potrzebowałem!
JSON C11
-1

Jako proste rozwiązanie możesz użyć: redux-obietnica

Ale jeśli używasz redux-thunk , powinieneś zrobić coś w ten sposób:

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

Mohammad P.
źródło