Mam działanie, które aktualizuje stan powiadomienia mojej aplikacji. Zwykle to powiadomienie będzie pewnego rodzaju błędem lub informacją. Muszę następnie wysłać inną akcję po 5 sekundach, która przywróci stan powiadomienia do początkowej, więc nie będzie żadnego powiadomienia. Głównym powodem tego jest zapewnienie funkcjonalności, w której powiadomienia znikają automatycznie po 5 sekundach.
Nie miałem szczęścia z użyciem setTimeout
i zwrotem innej akcji i nie mogę znaleźć sposobu, w jaki odbywa się to online. Wszelkie porady są mile widziane.
redux-saga
odpowiedzi, jeśli chcesz czegoś lepszego niż gromady. Późna odpowiedź, więc musisz przewijać dużo czasu, zanim zobaczysz, że się pojawia :) nie oznacza, że nie warto czytać. Oto skrót: stackoverflow.com/a/38574266/82609Odpowiedzi:
Nie wpadnij w pułapkę myślenia, że biblioteka powinna określać, jak zrobić wszystko . Jeśli chcesz zrobić coś z limitem czasu w JavaScript, musisz tego użyć
setTimeout
. Nie ma powodu, dla którego działania Redux powinny być inne.Redux nie oferują kilka alternatywnych sposobów radzenia sobie z asynchronicznym rzeczy, ale należy używać tylko tych, kiedy zdajesz sobie sprawę, powtarzamy za dużo kodu. Jeśli nie masz tego problemu, skorzystaj z oferty języka i wybierz najprostsze rozwiązanie.
Pisanie kodu asynchronicznego w linii
To zdecydowanie najprostszy sposób. I tutaj nie ma nic specyficznego dla Redux.
Podobnie z wnętrza podłączonego komponentu:
Jedyną różnicą jest to, że w podłączonym komponencie zwykle nie masz dostępu do samego sklepu, ale dostajesz jednego
dispatch()
lub konkretnych twórców akcji wstrzykiwanych jako rekwizyty. Nie ma to jednak dla nas żadnej różnicy.Jeśli nie lubisz tworzyć literówek podczas wywoływania tych samych akcji z różnych komponentów, możesz wyodrębnić twórców akcji zamiast wywoływać obiekty akcji bezpośrednio:
Lub, jeśli wcześniej związałeś je
connect()
:Do tej pory nie korzystaliśmy z oprogramowania pośredniego ani innych zaawansowanych koncepcji.
Wyodrębnianie Async Action Creator
Powyższe podejście działa dobrze w prostych przypadkach, ale może się okazać, że ma kilka problemów:
HIDE_NOTIFICATION
upływie pierwszego limitu czasu nastąpi wysłanie , które błędnie ukrywa drugie powiadomienie wcześniej niż po upływie limitu czasu.Aby rozwiązać te problemy, musisz wyodrębnić funkcję, która scentralizuje logikę limitu czasu i wywoła te dwie akcje. Może to wyglądać tak:
Teraz komponenty mogą korzystać
showNotificationWithTimeout
bez powielania tej logiki lub posiadania warunków wyścigu z różnymi powiadomieniami:Dlaczego
showNotificationWithTimeout()
akceptujedispatch
jako pierwszy argument? Ponieważ musi wysłać działania do sklepu. Zwykle komponent ma do niego dostęp,dispatch
ale ponieważ chcemy, aby funkcja zewnętrzna przejmowała kontrolę nad wysyłaniem, musimy dać mu kontrolę nad wysyłaniem.Jeśli wyeksportowałeś sklep z singletonem z jakiegoś modułu, możesz go po prostu zaimportować i
dispatch
bezpośrednio na nim:Wygląda to na prostsze, ale nie zalecamy tego podejścia . Głównym powodem, dla którego nam się nie podoba, jest to, że zmusza sklep do bycia singletonem . To bardzo utrudnia wdrożenie renderowania serwera . Na serwerze będziesz chciał, aby każde żądanie miało własny sklep, aby różni użytkownicy otrzymywali różne wstępnie załadowane dane.
Sklep singletonowy dodatkowo utrudnia testowanie. Nie można już kpić ze sklepu podczas testowania twórców akcji, ponieważ odnoszą się one do konkretnego prawdziwego sklepu wyeksportowanego z określonego modułu. Nie można nawet zresetować jego stanu z zewnątrz.
Więc chociaż technicznie możesz eksportować sklep singleton z modułu, odradzamy go. Nie rób tego, chyba że masz pewność, że Twoja aplikacja nigdy nie doda renderowania serwera.
Powrót do poprzedniej wersji:
To rozwiązuje problemy z powielaniem logiki i ratuje nas przed warunkami wyścigowymi.
Thunk Middleware
W przypadku prostych aplikacji podejście powinno wystarczyć. Nie martw się o oprogramowanie pośrednie, jeśli jesteś z niego zadowolony.
W większych aplikacjach mogą się jednak pojawić pewne niedogodności.
Na przykład wydaje się niefortunne, że musimy to omijać
dispatch
. Utrudnia to oddzielenie kontenera i komponentów prezentacji, ponieważ każdy komponent, który wywołuje akcje Redux asynchronicznie w powyższy sposób, musi zaakceptowaćdispatch
jako rekwizyt, aby mógł go przekazać dalej. Nie można już po prostu wiązać twórców akcji,connect()
ponieważshowNotificationWithTimeout()
tak naprawdę nie jest twórcą akcji. Nie zwraca akcji Redux.Ponadto może być niewygodne zapamiętanie, które funkcje są twórcami akcji synchronicznych,
showNotification()
a które asynchronicznymi pomocnikamishowNotificationWithTimeout()
. Musisz ich używać inaczej i uważaj, aby ich nie pomylić.To była motywacja do znalezienia sposobu na „legitymizowanie” tego schematu zapewniania
dispatch
funkcji pomocniczej i pomoc Redux „zobaczyć” takich twórców akcji asynchronicznych jako szczególny przypadek zwykłych twórców akcji, a nie całkowicie różnych funkcji.Jeśli nadal jesteś z nami i rozpoznajesz problem w swojej aplikacji, możesz skorzystać z oprogramowania pośredniego Redux Thunk .
Krótko mówiąc, Redux Thunk uczy Redux rozpoznawania specjalnych rodzajów akcji, które w rzeczywistości działają:
Gdy to oprogramowanie pośrednie jest włączone, jeśli wyślesz funkcję , oprogramowanie pośrednie Redux Thunk poda ją
dispatch
jako argument. Będzie także „połykać” takie akcje, więc nie martw się, że twoje reduktory otrzymają dziwne argumenty funkcji. Twoje reduktory będą otrzymywać tylko proste działania na obiektach - albo emitowane bezpośrednio, albo emitowane przez funkcje, które właśnie opisaliśmy.To nie wygląda bardzo przydatne, prawda? Nie w tej konkretnej sytuacji. Pozwala nam to jednak zadeklarować
showNotificationWithTimeout()
jako zwykłego twórcę akcji Redux:Zwróć uwagę, że funkcja jest prawie identyczna z tą, którą napisaliśmy w poprzedniej sekcji. Jednak nie przyjmuje tego
dispatch
jako pierwszego argumentu. Zamiast tego zwraca funkcję, która przyjmujedispatch
jako pierwszy argument.Jak wykorzystalibyśmy to w naszym komponencie? Zdecydowanie możemy napisać to:
Wzywamy twórcę działania asynchronicznego, aby uzyskać wewnętrzną funkcję, która chce właśnie
dispatch
, a następnie przechodzimydispatch
.Jest to jednak jeszcze bardziej niezręczne niż oryginalna wersja! Dlaczego w ogóle poszliśmy tą drogą?
Z powodu tego, co mówiłem wcześniej. Jeśli oprogramowanie pośrednie Redux Thunk jest włączone, za każdym razem, gdy spróbujesz wywołać funkcję zamiast obiektu akcji, oprogramowanie pośrednie wywoła tę funkcję z
dispatch
samą metodą jako pierwszy argument .Zamiast tego możemy to zrobić:
Wreszcie, wywołanie akcji asynchronicznej (tak naprawdę serii akcji) nie różni się niczym od synchronizacji pojedynczej akcji z komponentem. Co jest dobre, ponieważ komponenty nie powinny dbać o to, czy coś dzieje się synchronicznie czy asynchronicznie. Właśnie to wyabstrahowaliśmy.
Zauważ, że skoro „nauczyliśmy” Reduxa rozpoznawania takich „specjalnych” twórców akcji (nazywamy ich twórcami akcji „ thunk” ), możemy teraz używać ich w dowolnym miejscu, w którym używalibyśmy zwykłych twórców akcji. Możemy na przykład używać ich z
connect()
:Czytanie stanu w kępach
Zazwyczaj reduktory zawierają logikę biznesową do określania następnego stanu. Reduktory uruchamiają się jednak dopiero po wysłaniu akcji. Co się stanie, jeśli masz efekt uboczny (taki jak wywołanie interfejsu API) w kreatorze akcji thunk i chcesz temu zapobiec pod pewnymi warunkami?
Bez użycia grubego oprogramowania pośredniego wystarczy wykonać następujące sprawdzenie w składniku:
Jednak celem wyodrębnienia twórcy akcji było scentralizowanie tej powtarzalnej logiki w wielu komponentach. Na szczęście Redux Thunk oferuje sposób na odczytanie aktualnego stanu sklepu Redux. Oprócz
dispatch
tego przekazuje równieżgetState
jako drugi argument funkcji zwracanej przez twórcę akcji. Dzięki temu kolega może odczytać bieżący stan sklepu.Nie nadużywaj tego wzoru. Jest dobry do ratowania wywołań API, gdy dostępne są buforowane dane, ale nie jest to bardzo dobra podstawa do budowania logiki biznesowej. Jeśli używasz
getState()
tylko warunkowo, aby wywołać różne działania, rozważ umieszczenie logiki biznesowej w reduktorach.Następne kroki
Teraz, gdy masz podstawową intuicję na temat działania thunks, sprawdź przykład asynchroniczny Redux, który ich używa.
Możesz znaleźć wiele przykładów, w których thunks zwracają obietnice. Nie jest to wymagane, ale może być bardzo wygodne. Redux nie dba o to, co zwrócisz z thunk, ale daje ci wartość zwrotu
dispatch()
. Właśnie dlatego możesz zwrócić Obietnicę od dziadka i poczekać na jej wypełnienie, dzwoniącdispatch(someThunkReturningPromise()).then(...)
.Możesz także podzielić złożonych twórców akcji thunk na kilku mniejszych twórców akcji thunk.
dispatch
Metoda dostarczana przez łącznikami może zaakceptować łącznikami siebie, dzięki czemu można zastosować wzór rekurencyjnie. Ponownie działa to najlepiej w przypadku obietnic, ponieważ można zaimplementować asynchroniczny przepływ sterowania.W przypadku niektórych aplikacji możesz znaleźć się w sytuacji, w której wymagania dotyczące asynchronicznego przepływu sterowania są zbyt złożone, aby można je było wyrazić z grubsza. Na przykład ponawianie nieudanych żądań, przepływ ponownej autoryzacji za pomocą tokenów lub wdrażanie krok po kroku może być zbyt szczegółowe i podatne na błędy, jeśli zostanie napisane w ten sposób. W takim przypadku warto przyjrzeć się bardziej zaawansowanym rozwiązaniom asynchronicznego sterowania przepływem, takim jak Redux Saga lub Redux Loop . Oceń je, porównaj przykłady odpowiadające Twoim potrzebom i wybierz ten, który najbardziej Ci się podoba.
Wreszcie, nie używaj niczego (w tym thunks), jeśli nie potrzebujesz ich naprawdę. Pamiętaj, że w zależności od wymagań Twoje rozwiązanie może wyglądać tak prosto, jak
Nie przejmuj się, chyba że wiesz, dlaczego to robisz.
źródło
if (cond) dispatch({ type: 'A' }) else dispatch({ type: 'B' })
może powinieneś po prostudispatch({ type: 'C', something: cond })
i zignorować akcję w reduktorach zamiast w zależności odaction.something
i bieżącego stanu.Korzystanie z sagi Redux
Jak powiedział Dan Abramov, jeśli chcesz bardziej zaawansowanej kontroli nad kodem asynchronicznym, możesz rzucić okiem na redux-saga .
Ta odpowiedź jest prostym przykładem. Jeśli chcesz uzyskać lepsze wyjaśnienia, dlaczego redux-saga może być przydatny w twojej aplikacji, sprawdź tę inną odpowiedź .
Ogólna idea jest taka, że Redux-saga oferuje interpreter generatorów ES6, który pozwala łatwo pisać kod asynchroniczny, który wygląda jak kod synchroniczny (dlatego często znajdziesz skończoną pętlę w sadze Redux). W jakiś sposób Redux-saga buduje swój własny język bezpośrednio w Javascript. Na początku saga Redux może być nieco trudna do nauczenia, ponieważ potrzebujesz podstawowej wiedzy na temat generatorów, ale także rozumiesz język oferowany przez sagę Redux.
Spróbuję tutaj opisać tutaj system powiadomień, który zbudowałem na podstawie redux-sagi. Ten przykład obecnie działa w produkcji.
Zaawansowana specyfikacja systemu powiadomień
Wynik
Zrzut ekranu mojej aplikacji produkcyjnej Stample.co
Kod
Tutaj nazwałem powiadomienie,
toast
ale jest to szczegół nazewnictwa.I reduktor:
Stosowanie
Możesz po prostu wysłać
TOAST_DISPLAY_REQUESTED
zdarzenia. Jeśli wyślesz 4 żądania, zostaną wyświetlone tylko 3 powiadomienia, a czwarte pojawi się nieco później, gdy zniknie pierwsze powiadomienie.Pamiętaj, że nie polecam wysyłania
TOAST_DISPLAY_REQUESTED
z JSX. Wolisz dodać kolejną sagę, która słucha już istniejących zdarzeń aplikacji, a następnie wysłaćTOAST_DISPLAY_REQUESTED
: Twój komponent, który uruchamia powiadomienie, nie musi być ściśle powiązany z systemem powiadomień.Wniosek
Mój kod nie jest idealny, ale działa w produkcji z 0 błędami przez miesiące. Saga Redux i generatory są początkowo trochę trudne, ale kiedy je zrozumiesz, ten system jest dość łatwy do zbudowania.
Implementacja bardziej złożonych reguł, takich jak:
Szczerze mówiąc, powodzenia we wdrażaniu tego rodzaju rzeczy poprawnie z thunkami.
Zauważ, że możesz zrobić dokładnie to samo z obserwowalnymi redux, które są bardzo podobne do sagi redux. Jest prawie taki sam i jest kwestią gustu między generatorami a RxJS.
źródło
yield call(delay,timeoutValue);
: nie jest to ten sam interfejs API, ale ma ten sam efektRepozytorium z przykładowymi projektami
Obecnie istnieją cztery przykładowe projekty:
Przyjęta odpowiedź jest niesamowita.
Ale czegoś brakuje:
Utworzyłem więc repozytorium Hello Async, aby dodać brakujące rzeczy:
Saga Redux
Akceptowana odpowiedź zawiera już przykładowe fragmenty kodu dla Async Code Inline, Async Action Generator i Redux Thunk. W celu zapewnienia kompletności udostępniam fragmenty kodu dla Sagi Redux:
Działania są proste i czyste.
Nic nie jest wyjątkowe w przypadku komponentu.
Sagi oparte są na generatorach ES6
W porównaniu do Redux Thunk
Plusy
Cons
Jeśli powyższe fragmenty kodu nie odpowiadają na wszystkie pytania, zapoznaj się z działającym projektem .
źródło
Możesz to zrobić za pomocą redux-thunk . Jest przewodnikiem Redux dokumencie działań asynchronicznych jak setTimeout.
źródło
applyMiddleware(ReduxPromise, thunk)(createStore)
uzupełniające , kiedy używasz oprogramowania pośredniego, czy w ten sposób dodajesz kilka oprogramowania pośredniego (oddzielone przecinkami?), Ponieważ nie wydaje mi się, żeby działało.const store = createStore(reducer, applyMiddleware([ReduxPromise, thunk]));
Poleciłbym również przyjrzeć się wzorowi SAM .
Wzorzec SAM zaleca włączenie „predykatu następnego działania”, w którym (automatyczne) działania, takie jak „powiadomienia znikają automatycznie po 5 sekundach” są uruchamiane po aktualizacji modelu (model SAM ~ stan reduktora + sklep).
Wzorzec zaleca sekwencjonowanie akcji i mutacji modelu pojedynczo, ponieważ „stan kontrolny” modelu „kontroluje”, które akcje są włączone i / lub automatycznie wykonywane przez predykat następnej akcji. Po prostu nie można przewidzieć (ogólnie), w jakim stanie będzie system przed przetworzeniem akcji, a zatem czy następne oczekiwane działanie będzie dozwolone / możliwe.
Na przykład kod
nie byłoby dozwolone w SAM, ponieważ fakt, że można wywołać akcję hideNotification, zależy od tego, czy model pomyślnie zaakceptuje wartość „showNotication: true”. Mogą istnieć inne części modelu, które uniemożliwiają zaakceptowanie go, dlatego nie byłoby powodu, aby wywołać akcję hideNotification.
Zdecydowanie zaleciłbym wdrożenie poprawnego predykatu następnego działania po aktualizacji sklepu i poznaniu nowego stanu kontroli modelu. To najbezpieczniejszy sposób na wdrożenie pożądanego zachowania.
Możesz dołączyć do nas na Gitter, jeśli chcesz. Dostępny jest również przewodnik wprowadzający do SAM .
źródło
V = S( vm( M.present( A(data) ) ), nap(M))
jest po prostu piękna. Dziękujemy za podzielenie się swoimi przemyśleniami i doświadczeniem. Będę kopać głębiej.Po wypróbowaniu różnych popularnych podejść (twórcy akcji, gromady, sagi, epopeje, efekty, niestandardowe oprogramowanie pośrednie) nadal czułem, że być może jest miejsce na ulepszenia, więc udokumentowałem swoją podróż w tym artykule na blogu, Gdzie mam umieścić logikę biznesową aplikacja React / Redux?
Podobnie jak w przypadku dyskusji tutaj, próbowałem skontrastować i porównać różne podejścia. W końcu doprowadziło mnie to do wprowadzenia nowej biblioteki redux-logiki, która czerpie inspirację z eposów, sag, niestandardowego oprogramowania pośredniego.
Pozwala przechwytywać działania w celu sprawdzania poprawności, weryfikacji, autoryzacji, a także zapewnia sposób wykonywania asynchronicznych operacji we / wy.
Niektóre typowe funkcje można po prostu zadeklarować, jak ogłaszanie, ograniczanie, anulowanie i tylko przy użyciu odpowiedzi z ostatniego żądania (takeLatest). redux-logic otacza kod, zapewniając tę funkcjonalność dla Ciebie.
Dzięki temu możesz wdrożyć swoją podstawową logikę biznesową w dowolny sposób. Nie musisz używać obserwowalnych lub generatorów, chyba że chcesz. Używaj funkcji i wywołań zwrotnych, obietnic, funkcji asynchronicznych (asynchronizacja / oczekiwanie) itp.
Kod umożliwiający proste powiadomienie 5s wyglądałby mniej więcej tak:
W moim repozytorium mam bardziej zaawansowany przykład powiadomienia, który działa podobnie do opisanego przez Sebastiana Lorbera, w którym można ograniczyć wyświetlanie do N elementów i obracać je w kolejce. przykład powiadomienia redux-logic
Mam wiele przykładów live reds-logic jsfiddle, a także pełne przykłady . Nadal pracuję nad dokumentami i przykładami.
Chciałbym usłyszeć Twoją opinię.
źródło
Rozumiem, że to pytanie jest trochę stare, ale zamierzam wprowadzić inne rozwiązanie, używając obserwowalnej redux aka. Epicki.
Cytując oficjalną dokumentację:
Co można zaobserwować przy reduxie?
Epos jest podstawowym prymitywem obserwowalnego redux.
Krótko mówiąc, możesz utworzyć funkcję, która odbiera akcje za pośrednictwem Strumienia, a następnie zwraca nowy strumień akcji (używając typowych efektów ubocznych, takich jak przekroczenia limitu czasu, opóźnienia, interwały i żądania).
Pozwól, że opublikuję kod, a następnie wyjaśnię go nieco więcej
store.js
index.js
App.js
Kluczowy kod do rozwiązania tego problemu jest tak łatwy, jak widać, jedyną rzeczą, która wydaje się inna niż inne odpowiedzi, jest funkcja rootEpic.
Punkt 1. Podobnie jak w przypadku sag, musisz połączyć epopeję, aby uzyskać funkcję najwyższego poziomu, która odbiera strumień akcji i zwraca strumień akcji, dzięki czemu można go używać z fabryką oprogramowania pośredniego createEpicMiddleware . W naszym przypadku potrzebujemy tylko jednego, więc mamy tylko nasz rootEpic, więc nie musimy niczego łączyć, ale dobrze jest wiedzieć.
Punkt 2. Nasza rootEpic, która dba o logikę efektów ubocznych, zajmuje tylko około 5 linii kodu, co jest niesamowite! W tym fakt, że jest to prawie deklaratywne!
Punkt 3. Pierwiastek po linijce Wyjaśnienie (w komentarzach)
Mam nadzieję, że to pomoże!
źródło
switchMap
?Dlaczego to takie trudne? To tylko logika interfejsu użytkownika. Użyj dedykowanej akcji, aby ustawić dane powiadomień:
oraz dedykowany komponent do jego wyświetlenia:
W takim przypadku pytania powinny brzmieć „jak wyczyścić stary stan?”, „Jak powiadomić komponent o zmianie czasu”
Możesz zaimplementować akcję TIMEOUT, która jest wywoływana w setTimeout ze składnika.
Może wystarczy wyczyścić, gdy pojawi się nowe powiadomienie.
W każdym razie powinno
setTimeout
gdzieś być , prawda? Dlaczego nie zrobić tego w komponencieMotywacja polega na tym, że funkcja „zanikania powiadomień” jest naprawdę problemem dotyczącym interfejsu użytkownika. Upraszcza to testowanie logiki biznesowej.
Wydaje się, że nie ma sensu testować, jak jest zaimplementowany. Sensowne jest jedynie sprawdzenie, kiedy upłynie limit czasu powiadomienia. W ten sposób mniej kodu do kodu pośredniczącego, szybsze testy, czystszy kod.
źródło
Jeśli chcesz obsługiwać limity czasu dla akcji selektywnych, możesz wypróbować metodę oprogramowania pośredniego . Miałem podobny problem z selektywną obsługą akcji opartych na obietnicach, a to rozwiązanie było bardziej elastyczne.
Powiedzmy, że Twój twórca akcji wygląda następująco:
Limit czasu może zawierać wiele wartości w powyższej akcji
Implementacja oprogramowania pośredniego wygląda następująco:
Możesz teraz kierować wszystkie swoje działania przez tę warstwę oprogramowania pośredniego, używając funkcji redux.
Możesz znaleźć podobne przykłady tutaj
źródło
Odpowiednim sposobem na to jest użycie Redux Thunk, popularnego oprogramowania pośredniego dla Redux, zgodnie z dokumentacją Redux Thunk:
Zasadniczo zwraca więc funkcję i można opóźnić wysyłkę lub ustawić ją w stanie warunkowym.
Więc coś takiego zrobi dla ciebie zadanie:
źródło
To jest proste. Użyj pakietu trim-redux i napisz w ten
componentDidMount
lub inny sposób i zabij gocomponentWillUnmount
.źródło
Sam Redux jest dość obszerną biblioteką i do takich rzeczy musiałbyś użyć czegoś takiego jak Redux-thunk , który da
dispatch
funkcję, dzięki czemu będziesz mógł wysłać zamknięcie powiadomienia po kilku sekundach.Utworzyłem bibliotekę, aby rozwiązać takie problemy, jak gadatliwość i kompozycyjność, a twój przykład będzie wyglądał następująco:
Tworzymy więc akcje synchronizacji do wyświetlania powiadomień w akcji asynchronicznej, które mogą żądać informacji w tle lub sprawdzać później, czy powiadomienie zostało zamknięte ręcznie.
źródło