什么是Redux辑甜?
Redux是一個了不起的庫率翅。對于那些不知道Redux是什么的人來說,它是JavaScript應(yīng)用程序的可預(yù)測狀態(tài)容器沸久。在應(yīng)用中季眷,它可以作為應(yīng)用程序狀態(tài)的單一事實來源。調(diào)用狀態(tài)或Redux_存儲只能通過調(diào)度操作進行更改卷胯,調(diào)度操作_由 reducers處理子刮,后者根據(jù)調(diào)度的操作類型決定如何修改狀態(tài)。對于那些不熟悉Redux的人窑睁,請查看此鏈接挺峡。
現(xiàn)在,Redux最常與React結(jié)合使用担钮,雖然它不受它約束 - 它可以與任何其他視圖庫一起使用橱赠。
Redux的問題
但是,Redux有一個非常重要的問題 - 它本身并不能很好地處理異步操作箫津。這很糟糕狭姨,但另一方面,Redux只是一個庫鲤嫡,為您的應(yīng)用程序提供狀態(tài)管理,就像React只是一個視圖庫一樣绑莺。這些都不構(gòu)成一個完整的框架暖眼,您必須自己選擇其他的工具。有些人認(rèn)為這是一件壞事纺裁,因為沒有一種指定的規(guī)范诫肠,包括我在內(nèi)的一些人認(rèn)為它很好,因為你不受任何特定技術(shù)的束縛欺缘。這很好栋豫,因為每個人都可以選擇他們認(rèn)為最符合他們需求的技術(shù)。
處理異步操作
現(xiàn)在谚殊,有幾個庫提供Redux中間件來處理異步操作丧鸯。當(dāng)我第一次開始使用React和Redux時,我被分配的項目使用了Redux-Thunk嫩絮。Redux-Thunk允許您編寫返回函數(shù)而不是普通對象的動作創(chuàng)建者(默認(rèn)情況下丛肢,Redux中的所有操作都必須是普通對象),這反過來又允許您延遲調(diào)度某些操作剿干。
作為當(dāng)時React / Redux的初學(xué)者蜂怎,thunk非常棒。它們易于編寫和理解置尔,并且不需要任何其他功能 - 您基本上只是以不同的方式編寫動作創(chuàng)建者杠步。
但是,一旦你開始使用React和Redux進入工作流程,你就會意識到幽歼,雖然很容易使用朵锣,但是thunks并不是那么好,因為试躏,1猪勇。你最終可能會回調(diào)地獄,特別是在發(fā)出API請求時颠蕴,2泣刹。您要么使用業(yè)務(wù)邏輯填充回調(diào)函數(shù)或減少函數(shù)來處理數(shù)據(jù)(因為,老實說犀被,您不會每次都獲得格式完美的數(shù)據(jù)椅您,特別是如果您使用第三方API),以及3.它們不是真正可測試的(你必須使用間諜方法來檢查是否已使用正確的對象調(diào)用了調(diào)度)寡键。所以掀泳,我開始研究其他可能更適合的解決方案。那是我遇到Redux-Saga的時候西轩。
Redux Saga非常接近我想要的東西员舵。你能感覺它就像一個單線程的應(yīng)用一樣,它獨自負責(zé)副作用藕畔。這基本上意味著sagas與主應(yīng)用程序分開運行并監(jiān)聽調(diào)度操作 - 一旦調(diào)度該特定saga正在偵聽的操作马僻,它就會執(zhí)行一些產(chǎn)生副作用的代碼,如API調(diào)用注服。它還允許你從saga內(nèi)部dispatch其他action韭邓,而且很容易測試,因為sagas返回效果_是普通對象溶弟。聽起來不錯女淑,對嗎?
Redux-Saga為大多數(shù)開發(fā)人員提供了折中辜御,也是一個很大的折中方案 - 它利用了Javascript的generator功能鸭你,這些功能具有相當(dāng)陡峭的學(xué)習(xí)曲線。Redux Saga創(chuàng)作者使用JS這個強大的功能擒权,但是苇本,我覺得generator功能使用起來感覺很不自然,至少對我來說菜拓,即使我知道如何他們工作以及如何使用它們瓣窄,我只是無法實際使用它們。就像那個樂隊或歌手一樣纳鼎,當(dāng)他們在收音機上播放時你聽起來并沒有什么問題俺夕,但是你甚至不會考慮自己演奏它們裳凸。這就是為什么我繼續(xù)搜索異步處理Redux中間件的原因。
Redux-Saga不能很好地處理的另一件事是取消已經(jīng)調(diào)度的異步操作 - 例如API調(diào)用(Redux Observable由于其響應(yīng)特性而做得非常好)劝贸。
下一步
大約一個星期前姨谷,我正在看一個朋友和我為大學(xué)寫過的舊Android項目,并在那里看到了一些RxJava代碼映九,并自言自語:如果有一個Redux的Reactive中間件怎么辦梦湘?所以我做了一些研究,好吧件甥,眾神聽到了我的祈禱:Cue Redux Observable捌议。
那么什么是 Redux Observable?它是Redux的另一個中間件引有,它允許您以功能瓣颅,反應(yīng)和聲明的方式處理異步數(shù)據(jù)流。這是什么意思譬正?這意味著您編寫的代碼適用于異步數(shù)據(jù)流宫补。換句話說,您基本上會在這些流上監(jiān)聽新值(訂閱流*)并相應(yīng)地對這些值做出反應(yīng)曾我。
有關(guān)響應(yīng)式編程的最深入的指南粉怕,請查看此鏈接和此鏈接。兩者都對(功能)響應(yīng)式編程提供了非常好的概述抒巢,并為您提供了一個非常好的入門贫贝。
Redux Observable解決了哪些問題?
在查看新的庫/工具/框架時虐秦,最重要的問題是它如何幫助您完成工作平酿。一般來說凤优,Redux Observable所做的一切悦陋,Redux-Saga也是如此。它將您的邏輯移動到您的動作創(chuàng)建者之外筑辨,它在處理異步操作方面表現(xiàn)出色俺驶,并且易于測試。然而棍辕,在我的觀點中暮现,Redux Observable的整個工作流程感覺更自然,考慮到這兩者都有一個陡峭的學(xué)習(xí)曲線(generator和響應(yīng)式編程起初有點難以掌握楚昭,因為它們不僅需要學(xué)習(xí)而且還需要學(xué)習(xí)適應(yīng)你的思維方式)栖袋。
我們現(xiàn)在可以開始編碼嗎?
所以抚太,既然你知道什么是功能性反應(yīng)式編程塘幅,如果你像我一樣昔案,你真的很喜歡操作數(shù)據(jù)的感覺。是時候?qū)⑦@個概念應(yīng)用到您的React / Redux應(yīng)用程序了电媳。
首先踏揣,作為任何Redux中間件,您必須在創(chuàng)建商店時將其添加到Redux應(yīng)用程序中匾乓。
首先捞稿,安裝它,運行
npm install --save rxjs rxjs-compat redux-observable
或
yarn add rxjs rxjs-compat redux-observable
取決于您正在使用的工具拼缝。
現(xiàn)在娱局,Redux Observable的基礎(chǔ)是Epics。Epics與Redux-Saga中的sagas相似珍促,不同之處在于铃辖,不是等待動作調(diào)度并將動作委托給worker,而是暫停執(zhí)行猪叙,直到使用yield關(guān)鍵字進行相同類型的另一個動作娇斩,epics分別運行并且聽取一系列動作,然后在流上收到特定動作時作出反應(yīng)穴翩。主要組件是ActionsObservable
Redux-Observable犬第,它擴展了Observable
RxJS。此observable表示一系列操作芒帕,每次從應(yīng)用程序發(fā)送操作時歉嗓,都會將其添加到流中。
好的背蟆,讓我們首先創(chuàng)建我們的Redux存儲并向其添加Redux Observable中間件(小提醒鉴分,引導(dǎo)可以使用create-react-app
CLI 的React項目)。在我們確定我們已經(jīng)安裝了所有依賴項(redux, react-redux, rxjs, rxjs-compat, redux-observable
)后带膀,我們可以從修改我們的index.js
文件開始看起來像這樣
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { createStore, applyMiddleware } from 'redux';
import { createEpicMiddleware } from 'redux-observable';
import { Provider } from 'react-redux';
const epicMiddleware = createEpicMiddleware(rootEpic);
const store = createStore(rootReducer, applyMiddleware(epicMiddleware));
const appWithProvider = (
<Provider store={store}>
<App />
</Provider>
);
ReactDOM.render(appWithProvider, document.getElementById('root'));
你可能已經(jīng)注意到了志珍,我們錯過了rootEpic
和rootReducer
。不要擔(dān)心這個垛叨,我們稍后會添加它們÷着矗現(xiàn)在,讓我們來看看這里發(fā)生了什么:
首先嗽元,我們正在導(dǎo)入必要的功能來創(chuàng)建我們的商店并應(yīng)用我們的中間件敛纲。在那之后,我們使用createEpicMiddleware
Redux Observable創(chuàng)建我們的中間件剂癌,并將其傳遞給根epics(我們將在稍后介紹)淤翔。然后我們使用該createStore
函數(shù)創(chuàng)建我們的store并將其傳遞給root redurcer并將epics中間件應(yīng)用于store。
好的佩谷,現(xiàn)在我們已經(jīng)完成了所有設(shè)置旁壮,讓我們首先創(chuàng)建我們的root reducer辞做。創(chuàng)建一個新文件夾reducers
,其中包含一個名為的新文件root.js
寡具。將以下代碼添加到其中:
const initialState = {
whiskies: [], // for this example we'll make an app that fetches and lists whiskies
isLoading: false,
error: false
};
export default function rootReducer(state = initialState, action) {
switch (action.type) {
default:
return state;
}
}
任何熟悉Redux的人都已經(jīng)知道這里發(fā)生了什么 - 我們正在創(chuàng)建一個reducer函數(shù)秤茅,它接受state
和action
作為參數(shù),并根據(jù)動作類型返回一個新狀態(tài)(因為我們還沒有定義任何動作童叠,我們只是添加的default
塊框喳,并返回初始化狀態(tài))。
現(xiàn)在厦坛,返回到您的index.js
文件并添加以下導(dǎo)入:
import rootReducer from './reducers/root';
如您所見五垮,現(xiàn)在我們沒有關(guān)于rootReducer
不存在的錯誤。現(xiàn)在讓我們創(chuàng)造我們的root epics; 首先杜秸,創(chuàng)建一個新文件夾epics
并在其中創(chuàng)建一個名為的文件index.js
放仗。在其中,暫時添加以下代碼:
import { combineEpics } from 'redux-observable';
export const rootEpic = combineEpics();
這里我們只是使用combineEpics
Redux Observable 提供的函數(shù)來組合我們的(現(xiàn)在撬碟,不存在)epics诞挨,并將該值賦給我們導(dǎo)出的常量。我們現(xiàn)在應(yīng)該index.js
通過簡單地添加以下導(dǎo)入來修復(fù)條目文件中的其他錯誤:
import { rootEpic } from './epics';
不錯呢蛤,現(xiàn)在我們處理了所有配置惶傻,我們可以定義我們可以dispatch的action類型以及這些action creator。
首先其障,index.js
在里面創(chuàng)建一個名為actions和一個文件的新文件夾银室。
(注意:對于大型的生產(chǎn)級的項目,您應(yīng)該以合理的方式對action励翼,reducer和epics進行分組蜈敢,而不是將它們?nèi)糠旁谝粋€文件中,但是汽抚,由于我們的應(yīng)用程序非常小抓狭,因此沒有任何意義)
在我們開始編寫代碼之前,讓我們考慮一下我們可以調(diào)度的action類型殊橙。通常辐宾,我們需要一個action來通知Redux / Redux-Observable它應(yīng)該開始獲取epics狱从,讓我們調(diào)用該動作FETCH_WHISKIES膨蛮。由于這是一個異步操作,我們不知道它究竟何時完成季研,因此我們將希望在調(diào)用成功完成時調(diào)度FETCH_WHISKIES_SUCCESS操作敞葛。以類似的方式,由于這是一個API調(diào)用与涡,它可能會失敗惹谐,我們希望通過消息通知我們的用戶持偏,因此我們將調(diào)度FETCH_WHISKIES_FAILURE操作并通過顯示錯誤消息來處理它。
讓我們在代碼中定義這些動作(及其動作創(chuàng)建者):
export const FETCH_WHISKIES = 'FETCH_WHISKYS';
export const FETCH_WHISKIES_SUCCESS = 'FETCH_WHISKYS_SUCCESS';
export const FETCH_WHISKIES_FAILURE = 'FETCH_WHISKYS_FAILURE';
export const fetchWhiskies = () => ({
type: FETCH_WHISKIES,
});
export const fetchWhiskiesSuccess = (whiskies) => ({
type: FETCH_WHISKIES_SUCCESS,
payload: whiskies
});
export const fetchWhiskiesFailure = (message) => ({
type: FETCH_WHISKIES_FAILURE,
payload: message
});
對于那些不清楚我在這里做什么的人氨肌,我只是為動作類型定義常量鸿秆,然后使用ES6的lambda簡寫符號我創(chuàng)建箭頭函數(shù),返回包含類型和(可選)普通對象屬性怎囚。該類型用于標(biāo)識已分派的操作類型卿叽,有效負載是在調(diào)度操作時將數(shù)據(jù)發(fā)送到Reducer(和存儲)的方式。
現(xiàn)在我們已經(jīng)創(chuàng)建了我們的action和action creator恳守,讓我們在reducer中處理這些動作:
更新您reducers/index.js
的以下內(nèi)容考婴。
import {
FETCH_WHISKIES,
FETCH_WHISKIES_FAILURE,
FETCH_WHISKIES_SUCCESS
} from '../actions';
const initialState = {
whiskies: [],
isLoading: false,
error: null
};
export default function rootReducer(state = initialState, action) {
switch (action.type) {
case FETCH_WHISKIES:
return {
...state,
// whenever we want to fetch the whiskies, set isLoading to true to show a spinner
isLoading: true,
error: null
};
case FETCH_WHISKIES_SUCCESS:
return {
whiskies: [...action.payload],
// whenever the fetching finishes, we stop showing the spinner and then show the data
isLoading: false,
error: null
};
case FETCH_WHISKIES_FAILURE:
return {
whiskies: [],
isLoading: false,
// same as FETCH_WHISKIES_SUCCESS, but instead of data we will show an error message
error: action.payload
};
default:
return state;
}
}
現(xiàn)在我們已經(jīng)完成了所有這些,我們可以最終編寫一些Redux-Observable代碼(抱歉花了這么長時間4吆妗)
轉(zhuǎn)到您的epics/index.js
文件沥阱,讓我們創(chuàng)建我們的第一部史詩。首先伊群,您需要添加一些導(dǎo)入:
import { Observable } from 'rxjs';
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/operator/map';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/catch';
import { ajax } from 'rxjs/observable/dom/ajax';
import {
FETCH_WHISKIES,
fetchWhiskiesFailure,
fetchWhiskiesSuccess
} from "../actions";
我們在這里做的是導(dǎo)入我們需要調(diào)度的action crator以及我們需要在動作流中觀察的action type考杉,以及來自RxJS的一些操作符以及Observable
。請注意舰始,RxJS和Redux Observable都不會自動導(dǎo)入運算符奔则,因此您必須自己導(dǎo)入它們(另一種選擇是在您的條目index.js中導(dǎo)入整個'rxjs'模塊,但我不建議這樣做蔽午,因為它會給你大束尺寸)易茬。好的,讓我們來看看我們導(dǎo)入的這些運算符以及它們的作用:
map
- 類似于Javascript的本機Array.map()
及老,map
在流中的每個項目上執(zhí)行一個函數(shù)抽莱,并返回帶有映射項目的新流/ Observable。
of
- 從非Observable值創(chuàng)建一個Observable / stream(它可以是一個原語骄恶,一個對象食铐,一個函數(shù),任何東西)僧鲁。
ajax
- 是提供的用于執(zhí)行AJAX請求的RxJS模塊; 我們將使用它來調(diào)用API虐呻。
catch
- 用于捕獲可能發(fā)生的任何錯誤
switchMap
- 這是最復(fù)雜的。它的作用是寞秃,它接受一個返回Observable的函數(shù)斟叼,每次這個內(nèi)部Observable發(fā)出一個值時,它會將該值合并到外部Observable(調(diào)用switchMap的那個)春寿。這是捕獲朗涩,每次創(chuàng)建一個新的內(nèi)部Observable時,外部Observable都會訂閱它(即偵聽值并將它們合并到自身)绑改,并取消對先前發(fā)出的Observable的所有其他訂閱谢床。這對于我們不關(guān)心先前結(jié)果是否已成功或已被取消的情況非常有用兄一。例如,當(dāng)我們發(fā)送多個用于獲取威士忌的操作時识腿,我們只需要最新的結(jié)果出革,switchMap就是這樣做的,它將訂閱最新的結(jié)果并將其合并到外部Observable并丟棄之前的請求(如果它們?nèi)晕赐瓿桑┒伤稀T趧?chuàng)建POST請求時蹋盆,您通常關(guān)心先前的請求是否已完成,以及是否使用了mergeMap硝全。mergeMap
做同樣的事情栖雾,除非它沒有取消先前的Observables。
考慮到這一點伟众,讓我們看看用于獲取威士忌的Epic將如何顯示:
const url = 'https://evening-citadel-85778.herokuapp.com/whiskey/'; // The API for the whiskies
/*
The API returns the data in the following format:
{
"count": number,
"next": "url to next page",
"previous": "url to previous page",
"results: array of whiskies
}
since we are only interested in the results array we will have to use map on our observable
*/
function fetchWhiskiesEpic(action$) { // action$ is a stream of actions
// action$.ofType is the outer Observable
return action$
.ofType(FETCH_WHISKIES) // ofType(FETCH_WHISKIES) is just a simpler version of .filter(x => x.type === FETCH_WHISKIES)
.switchMap(() => {
// ajax calls from Observable return observables. This is how we generate the inner Observable
return ajax
.getJSON(url) // getJSON simply sends a GET request with Content-Type application/json
.map(data => data.results) // get the data and extract only the results
.map(whiskies => whiskies.map(whisky => ({
id: whisky.id,
title: "whisky.title,"
imageUrl: whisky.img_url
})))// we need to iterate over the whiskies and get only the properties we need
// filter out whiskies without image URLs (for convenience only)
.map(whiskies => whiskies.filter(whisky => !!whisky.imageUrl))
// at the end our inner Observable has a stream of an array of whisky objects which will be merged into the outer Observable
})
.map(whiskies => fetchWhiskiesSuccess(whiskies)) // map the resulting array to an action of type FETCH_WHISKIES_SUCCESS
// every action that is contained in the stream returned from the epic is dispatched to Redux, this is why we map the actions to streams.
// if an error occurs, create an Observable of the action to be dispatched on error. Unlike other operators, catch does not explicitly return an Observable.
.catch(error => Observable.of(fetchWhiskiesFailure(error.message)))
}
在此之后析藕,還剩下一件事,那就是將我們的史詩添加到combineEpics
函數(shù)調(diào)用中凳厢,如下所示:
export const rootEpic = combineEpics(fetchWhiskiesEpic);
好的账胧,這里有很多事,我會給你的先紫。但讓我們一塊一塊地分開治泥。
ajax.getJSON(url)
返回一個Observable,其中包含來自請求的數(shù)據(jù)作為流中的值遮精。
.map(data => data.results)
從Observable獲取所有值(在這種情況下只有1)居夹,results
從響應(yīng)中獲取屬性并返回帶有新值的新Observable(即只有results
數(shù)組)。
.map(whiskies => whiskies.map(whisky => ({
id: whisky.id,
title: "whisky.title,"
imageUrl: whisky.img_url
})))
從前一個observable(結(jié)果數(shù)組)獲取值本冲,調(diào)用Array.map()
它准脂,并映射數(shù)組的每個元素(每個威士忌)以創(chuàng)建一個新的對象數(shù)組,只保存每個威士忌的id檬洞,title和imageUrl狸膏,因為我們不需要別的。
.map(whiskies => whiskies.filter(whisky => !!whisky.imageUrl))
獲取Observable中的數(shù)組并返回帶有已過濾數(shù)組的新Observable添怔。
在switchMap
一個包裝此代碼借此觀察到的湾戳,內(nèi)可觀測的流合并到調(diào)用可觀察到的數(shù)據(jù)流switchMap
。如果另一個威士忌提取請求通過广料,則此操作將再次重復(fù)砾脑,之前的結(jié)果將被丟棄,這要歸功于switchMap
性昭。
.map(whiskies => fetchWhiskiesSuccess(whiskies))
只需獲取我們添加到流中的這個新值拦止,并將其映射到FETCH_WHISKIES_SUCCESS類型的操作县遣,該操作將在從Epic返回Observable后調(diào)度糜颠。
.catch(error => Observable.of(fetchWhiskiesFailure(error.message)))
捕獲可能發(fā)生的任何錯誤汹族,只返回一個Observable。然后其兴,此Observable通過switchMap傳播顶瞒,再次將其合并到外部Observable,我們在流中獲得類型為FETCH_WHISKIES_FAILURE的操作元旬。
花點時間榴徐,這是一個復(fù)雜的過程,如果你還沒有碰過Reactive編程和RxJS匀归,它看起來和聲音都非晨幼剩可怕(閱讀我上面提供的那些鏈接!)穆端。
在此之后袱贮,我們需要做的就是渲染一個UI,它將有一個用于調(diào)度操作的按鈕和一個用于顯示數(shù)據(jù)的表体啰。我們這樣做; 首先創(chuàng)建一個名為components的新文件夾和一個名為Whisky.jsx的新組件攒巍。
import React from 'react';
const Whisky = ({ whisky }) => (
<div>
<img style={{ width: '300px', height: '300px' }} src={whisky.imageUrl} />
<h3>{whisky.title}</h3>
</div>
);
export default Whisky;
該組件只需呈現(xiàn)單個威士忌項目,其圖像和標(biāo)題荒勇。(請為了上帝的愛柒莉,永遠不要使用內(nèi)聯(lián)樣式。我在這里做它們因為它是一個簡單的例子)沽翔。
現(xiàn)在我們要渲染一個威士忌元素網(wǎng)格兢孝。讓我們創(chuàng)建一個名為WhiskyGrid.jsx的新組件。
import React from 'react';
import Whisky from './Whisky';
const WhiskyGrid = ({ whiskies }) => (
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr' }}>
{whiskies.map(whisky => (<Whisky key={whisky.id} whisky={whisky} />))}
</div>
);
export default WhiskyGrid;
WhiskyGrid所做的是利用CSS-Grid并創(chuàng)建每行3個元素的網(wǎng)格仅偎,只需將威士忌數(shù)組作為道具傳入西潘,并將每個威士忌映射到威士忌組件。
現(xiàn)在讓我們來看看我們的App.js:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import './App.css';
import { fetchWhiskies } from './actions';
import WhiskyGrid from './components/WhiskyGrid';
class App extends Component {
render() {
const {
fetchWhiskies,
isLoading,
error,
whiskies
} = this.props;
return (
<div class>
<button onClick={fetchWhiskies}>Fetch whiskies</button>
{isLoading && <h1>Fetching data</h1>}
{!isLoading && !error && <WhiskyGrid whiskies={whiskies} />}
{error && <h1>{error}</h1>}
</div>
);
}
}
const mapStateToProps = state => ({ ...state });
const mapDispatchToProps = dispatch =>
bindActionCreators({
fetchWhiskies
}, dispatch);
export default connect(mapStateToProps, mapDispatchToProps)(App);
如您所見哨颂,這里有很多修改喷市。首先,我們必須將Redux存儲和動作創(chuàng)建器綁定到組件的props威恼。我們使用connect
react-redux中的HOC來實現(xiàn)這一目標(biāo)品姓。之后,我們創(chuàng)建一個div箫措,它有一個按鈕腹备,其onClick設(shè)置為調(diào)用fetchWhiskies動作創(chuàng)建者,現(xiàn)在綁定到dispatch
斤蔓。單擊該按鈕將調(diào)度FETCH_WHISKIES操作植酥,我們的Redux Observable epic將拾取它,從而調(diào)用API。接下來我們有一個條件友驮,如果Redux存儲中的isLoading屬性為true(已調(diào)度FETCH_WHISKIES但既未完成也未拋出錯誤)漂羊,我們將顯示一條文本,說明加載數(shù)據(jù)卸留。如果數(shù)據(jù)未加載且沒有錯誤走越,我們將渲染WhiskyGrid
組件并將Redux中的威士忌作為道具傳遞。如果error不為null耻瑟,則呈現(xiàn)錯誤消息旨指。
結(jié)論
響應(yīng)式并不容易。它提出了一種完全不同的編程范式喳整,它迫使你以不同的方式思考谆构。我不會說功能性比面向?qū)ο蟾茫蛘哒fReactive是最好的框都。最好的編程范式低淡,IN MY OPINION,是范式的組合瞬项。但是蔗蹋,我相信Redux Observable確實提供了其他異步Redux中間件的絕佳替代方案,在您通過學(xué)習(xí)曲線之后囱淋,您將獲得一種處理異步事件的神奇猪杭,自然的方法。