当前位置: 首页 > 编程日记 > 正文

redux rxjs_可观察的RxJS和Redux入门指南

redux rxjs

Redux-Observable is an RxJS-based middleware for Redux that allows developers to work with async actions. It's an alternative to redux-thunk and redux-saga.

Redux-Observable是Redux的基于RxJS的中间件,允许开发人员使用异步操作。 它是redux-thunk和redux-saga的替代方案。

This article covers the basics of RxJS, how to setup Redux-Observables, and some of its practical use-cases. But before that, we need to understand the Observer Pattern.

本文介绍了RxJS的基础知识,如何设置Redux-Observables及其一些实际用例。 但是在此之前,我们需要了解Observer Pattern

观察者模式 (Observer Pattern)

In Observer pattern, an object called "Observable" or "Subject", maintains a collection of subscribers called "Observers." When the subjects' state changes, it notifies all its Observers.

在观察者模式中,称为“可观察”或“主题”的对象维护着称为“观察者”的订户的集合。 当主题的状态更改时,它会通知其所有观察者。

In JavaScript, the simplest example would be event emitters and event handlers.

在JavaScript中,最简单的示例是事件发射器和事件处理程序。

When you do .addEventListener, you are pushing an observer into the subject's collection of observers. Whenever the event happens, the subject notifies all the observers.

当您执行.addEventListener.addEventListener观察者推入主题的观察者集合中。 每当事件发生时,主题都会通知所有观察者。

RxJS (RxJS)

As per the official website,

根据官方网站,

RxJS is JavaScript implementation of ReactiveX, a library for composing asynchronous and event-based programs by using observable sequences.

RxJS是ReactiveX的 JavaScript实现, ReactiveX是一个库,用于通过使用可观察的序列来组成异步和基于事件的程序。

In simple terms, RxJS is an implementation of the Observer pattern. It also extends the Observer pattern by providing operators that allow us to compose Observables and Subjects in a declarative manner.

简单来说,RxJS是Observer模式的实现。 它还通过提供允许我们以声明方式撰写Observable和Subject的运算符,扩展了Observer模式。

Observers, Observables, Operators, and Subjects are the building blocks of RxJS. So let's look at each one in more detail now.

观察者,可观察者,运算符和主题是RxJS的构建块。 因此,让我们现在更详细地看看每个。

观察者 (Observers)

Observers are objects that can subscribe to Observables and Subjects. After subscribing, they can receive notifications of three types - next, error, and complete.

观察者是可以订阅“观察对象”和“主题”的对象。 订阅后,他们可以接收三种类型的通知-下一步,错误和完成。

Any object with the following structure can be used as an Observer.

具有以下结构的任何对象都可以用作观察者。

interface Observer<T> {closed?: boolean;next: (value: T) => void;error: (err: any) => void;complete: () => void;
}

When the Observable pushes next, error, and complete notifications, the Observer's .next, .error, and .complete methods are invoked.

当Observable推送next,error和complete通知时,将调用Observer的.next.error.complete方法。

可观察的 (Observables)

Observables are objects that can emit data over a period of time. It can be represented using the "marble diagram".

可观察对象是可以在一段时间内发出数据的对象。 可以使用“大理石图”表示。

Where the horizontal line represents the time, the circular nodes represent the data emitted by the Observable, and the vertical line indicates that the Observable has completed successfully.

水平线表示时间,圆形节点表示Observable发出的数据,垂直线表示Observable已成功完成。

Observables may encounter an error. The cross represents the error emitted by the Observable.

观察对象可能会遇到错误。 叉号表示Observable发出的误差。

The "completed" and "error" states are final. That means, Observables cannot emit any data after completing successfully or encountering an error.

“完成”和“错误”状态是最终状态。 这意味着,可观察对象在成功完成或遇到错误后无法发出任何数据。

创建一个可观察的 (Creating an Observable)

Observables are created using the new Observable constructor that takes one argument - the subscribe function. Observables can also be created using some operators, but we will talk about that later when we talk about Operators.

Observable使用new Observable构造函数创建,该构造函数带有一个参数-subscription函数。 还可以使用某些运算符来创建可观察对象,但是稍后将在讨论运算符时再进行讨论。

import { Observable } from 'rxjs';const observable = new Observable(subscriber => {// Subscribe function 
});

订阅可观察的 (Subscribing to an Observable)

Observables can be subscribed using their .subscribe method and passing an Observer.

可以使用它们的.subscribe方法预订观察者,并传递观察者。

observable.subscribe({next: (x) => console.log(x),error: (x) => console.log(x),complete: () => console.log('completed');
});

可观察者的执行 (Execution of an Observable)

The subscribe function that we passed to the new Observable constructor is executed every time the Observable is subscribed.

每次订阅Observable时,都会执行传递给new Observable构造函数的new Observable函数。

The subscribe function takes one argument - the Subscriber. The Subscriber resembles the structure of an Observer, and it has the same 3 methods: .next, .error, and .complete.

subscription函数采用一个参数-Subscriber。 订阅服务器类似于观察者的结构,并且具有相同的3种方法: .next.error.complete

Observables can push data to the Observer using the .next method. If the Observable has completed successfully, it can notify the Observer using the .complete method. If the Observable has encountered an error, it can push the error to the Observer using the .error method.

Observable可以使用.next方法将数据推送到Observer。 如果Observable成功完成,则可以使用.complete方法通知Observer。 如果Observable遇到错误,则可以使用.error方法将错误推送到Observer。

// Create an Observable
const observable = new Observable(subscriber => {subscriber.next('first data');subscriber.next('second data');setTimeout(() => {subscriber.next('after 1 second - last data');subscriber.complete();subscriber.next('data after completion'); // <-- ignored}, 1000);subscriber.next('third data');
});// Subscribe to the Observable
observable.subscribe({next: (x) => console.log(x),error: (x) => console.log(x),complete: () => console.log('completed')
});// Outputs:
//
// first data
// second data
// third data
// after 1 second - last data
// completed

可观察的是单播的 (Observables are Unicast)

Observables are unicast, which means Observables can have at most one subscriber. When an Observer subscribes to an Observable, it gets a copy of the Observable that has its own execution path, making the Observables unicast.

可观测值是单播的 ,这意味着可观测值最多可以有一个订户。 当观察者订阅一个Observable时,它将获得具有自己执行路径的Observable副本,从而使Observables单播。

It is like watching a YouTube video. All viewers watch the same video content but, they can be at watching different segments of the video.

就像观看YouTube视频一样。 所有观看者都观看相同的视频内容,但是他们可以观看视频的不同片段。

Example: let us create an Observable that emits 1 to 10 over 10 seconds. Then, subscribe to the Observable once immediately, and again after 5 seconds.

示例 :让我们创建一个Observable,它在10秒钟内发射1到10。 然后,立即订阅一次Observable,然后在5秒钟后再次订阅。

// Create an Observable that emits data every second for 10 seconds
const observable = new Observable(subscriber => {let count = 1;const interval = setInterval(() => {subscriber.next(count++);if (count > 10) {clearInterval(interval);   }}, 1000);
});// Subscribe to the Observable
observable.subscribe({next: value => {console.log(`Observer 1: ${value}`);}
});// After 5 seconds subscribe again
setTimeout(() => {observable.subscribe({next: value => {console.log(`Observer 2: ${value}`);}});
}, 5000);/* OutputObserver 1: 1
Observer 1: 2
Observer 1: 3
Observer 1: 4
Observer 1: 5
Observer 2: 1
Observer 1: 6
Observer 2: 2
Observer 1: 7
Observer 2: 3
Observer 1: 8
Observer 2: 4
Observer 1: 9
Observer 2: 5
Observer 1: 10
Observer 2: 6
Observer 2: 7
Observer 2: 8
Observer 2: 9
Observer 2: 10*/

In the output, you can notice that the second Observer started printing from 1 even though it subscribed after 5 seconds. This happens because the second Observer received a copy of the Observable whose subscribe function was invoked again. This illustrates the unicast behaviour of Observables.

在输出中,您可以注意到第二个观察者从5开始打印,即使它在5秒后订阅了。 发生这种情况是因为第二个Observer收到了Observable的副本,该副本的订阅函数再次被调用。 这说明了可观察对象的单播行为。

科目 (Subjects)

A Subject is a special type of Observable.

主题是可观察的一种特殊类型。

创建一个主题 (Creating a Subject)

A Subject is created using the new Subject constructor.

使用new Subject构造函数创建new Subject

import { Subject } from 'rxjs';// Create a subject
const subject = new Subject();

订阅主题 (Subscribing to a Subject)

Subscribing to a Subject is similar to subscribing to an Observable: you use the .subscribe method and pass an Observer.

订阅主题类似于订阅Observable:使用.subscribe方法并传递一个Observer。

subject.subscribe({next: (x) => console.log(x),error: (x) => console.log(x),complete: () => console.log("done")
});

主题的执行 (Execution of a Subject)

Unlike Observables, a Subject calls its own .next, .error, and .complete methods to push data to Observers.

与Observables不同,Subject调用自己的.next.error.complete方法将数据推送到Observers。

// Push data to all observers
subject.next('first data');// Push error to all observers
subject.error('oops something went wrong');// Complete
subject.complete('done');

主题是多播的 (Subjects are Multicast)

Subjects are multicast: multiple Observers share the same Subject and its execution path. It means all notifications are broadcasted to all the Observers. It is like watching a live program. All viewers are watching the same segment of the same content at the same time.

主题是多播的:多个观察者共享相同的主题及其执行路径。 这意味着所有通知都会广播到所有观察者。 就像观看直播节目一样。 所有观众都在同一时间观看同一内容的同一片段。

Example: let us create a Subject that emits 1 to 10 over 10 seconds. Then, subscribe to the Observable once immediately, and again after 5 seconds.

示例:让我们创建一个在10秒钟内发射1到10的主题。 然后,立即订阅一次Observable,然后在5秒钟后再次订阅。

// Create a subject
const subject = new Subject();let count = 1;
const interval = setInterval(() => {subscriber.next(count++);if (count > 10) {clearInterval(interval);}
}, 1000);// Subscribe to the subjects
subject.subscribe(data => {console.log(`Observer 1: ${data}`);
});// After 5 seconds subscribe again
setTimeout(() => {subject.subscribe(data => {console.log(`Observer 2: ${data}`);});
}, 5000);/* OUTPUTObserver 1: 1
Observer 1: 2
Observer 1: 3
Observer 1: 4
Observer 1: 5
Observer 2: 5
Observer 1: 6
Observer 2: 6
Observer 1: 7
Observer 2: 7
Observer 1: 8
Observer 2: 8
Observer 1: 9
Observer 2: 9
Observer 1: 10
Observer 2: 10*/

In the output, you can notice that the second Observer started printing from 5 instead of starting from 1. This happens because the second Observer is sharing the same Subject. Since it subscribed after 5 seconds, the Subject has already finished emitting 1 to 4. This illustrates the multicast behavior of a Subject.

在输出中,您会注意到第二个Observer从5开始打印,而不是从1开始。这是因为第二个Observer共享相同的Subject。 由于它在5秒钟后订阅,因此主体已经完成发射1到4。这说明了主体的多播行为。

主题既可观察又可观察 (Subjects are both Observable and Observer)

Subjects have the .next, .error and .complete methods. That means that they follow the structure of Observers. Hence, a Subject can also be used as an Observer and passed to the .subscribe function of Observables or other Subjects.

主题具有.next.error.complete方法。 这意味着它们遵循观察者的结构。 因此,主题也可以用作观察者,并传递给Observables或其他主题的.subscribe函数。

Example: let us create an Observable and a Subject. Then subscribe to the Observable using the Subject as an Observer. Finally, subscribe to the Subject. All the values emitted by the Observable will be pushed to the Subject, and the Subject will broadcast the received values to all its Observers.

示例:让我们创建一个Observable和Subject。 然后使用主题作为观察者订阅可观察对象。 最后,订阅主题。 Observable发出的所有值将被推送到Subject,并且Subject将把接收到的值广播到其所有Observer。

// Create an Observable that emits data every second
const observable = new Observable(subscriber => {let count = 1;const interval = setInterval(() => {subscriber.next(count++);if (count > 5) {clearInterval(interval);   }}, 1000);
});// Create a subject
const subject = new Subject();// Use the Subject as Observer and subscribe to the Observable
observable.subscribe(subject);// Subscribe to the subject
subject.subscribe({next: value => console.log(value)
});/* Output1
2
3
4
5*/

经营者 (Operators)

Operators are what make RxJS useful. Operators are pure functions that return a new Observable. They can be categorized into 2 main categories:

运算符使RxJS有用。 运算符是返回新的Observable的纯函数。 它们可以分为2个主要类别:

  1. Creation Operators

    创建运算符
  2. Pipeable Operators

    管道运算符

创建运算符 (Creation Operators)

Creation Operators are functions that can create a new Observable.

创建运算符是可以创建新的Observable的函数。

Example: we can create an Observable that emits each element of an array using the from operator.

示例:我们可以创建一个Observable,它使用from运算符发出数组的每个元素。

const observable = from([2, 30, 5, 22, 60, 1]);observable.subscribe({next: (value) => console.log("Received", value),error: (err) => console.log(err),complete: () => console.log("done")
});/* OUTPUTSReceived 2
Received 30
Received 5
Received 22
Received 60
Received 1
done*/

The same can be an Observable using the marble diagram.

使用大理石图可以观察到的结果相同。

管道运算符 (Pipeable Operators)

Pipeable Operators are functions that take an Observable as an input and return a new Observable with modified behavior.

管道运算符是将Observable用作输入并返回具有已修改行为的新Observable的函数。

Example: let's take the Observable that we created using the from operator. Now using this Observable, we can to create a new Observable that emits only numbers greater than 10 using the filter operator.

示例:让我们使用使用from运算符创建的Observable。 现在使用此Observable,我们可以使用filter运算符创建一个仅发出大于10的数字的新Observable。

const greaterThanTen = observable.pipe(filter(x => x > 10));greaterThanTen.subscribe(console.log, console.log, () => console.log("completed"));// OUTPUT
// 11
// 12
// 13
// 14
// 15

The same can be represented using the marble diagram.

可以使用大理石图表示相同的内容。

There are many more useful operators out there. You can see the full operators list along with examples on the official RxJS documentation here.

还有更多有用的运算符。 您可以在此处查看完整的运算符列表以及RxJS官方文档中的示例。

It is crucial to understand all the commonly used operators. Here are some operators that I use often:

了解所有常用的运算符至关重要。 这是我经常使用的一些运算符:

  1. mergeMap

    mergeMap

  2. switchMap

    switchMap

  3. exhaustMap

    exhaustMap

  4. map

    map

  5. catchError

    catchError

  6. startWith

    startWith

  7. delay

    delay

  8. debounce

    debounce

  9. throttle

    throttle

  10. interval

    interval

  11. from

    from

  12. of

    of

Redux可观察物 (Redux Observables)

As per the official website,

根据官方网站,

RxJS-based middleware for Redux. Compose and cancel async actions to create side effects and more.

基于RxJS的Redux中间件。 撰写和取消异步操作以创建副作用等。

In Redux, whenever an action is dispatched, it runs through all the reducer functions and a new state is returned.

在Redux中,每当分派动作时,它就会通过所有reducer函数运行并返回新状态。

Redux-observable takes all these dispatched actions and new states and creates two observables out of it - Actions observable action$, and States observable state$.

Redux-observable采取所有这些调度的动作和新状态,并在其中创建两个可观察对象-Actions observable action$和States observable state$

Actions observable will emit all the actions that are dispatched using the store.dispatch(). States observable will emit all the new state objects returned by the root reducer.

可观察到的动作将发出使用store.dispatch()调度的所有动作。 可观察到的状态将发出由根减速器返回的所有新状态对象。

史诗 (Epics)

As per the official website,

根据官方网站,

It is a function which takes a stream of actions and returns a stream of actions. Actions in, actions out.

该函数需要一系列操作并返回一系列操作。 行动,行动。

Epics are functions that can be used to subscribe to Actions and States Observables. Once subscribed, epics will receive the stream of actions and states as input, and it must return a stream of actions as an output. Actions In - Actions Out.

史诗是可用于订阅“动作”和“可观察状态”的功能。 订阅后,史诗将接收动作和状态流作为输入,并且它必须返回动作流作为输出。 行动在行动在行动

const someEpic = (action$, state$) => { return action$.pipe( // subscribe to actions observablemap(action => { // Receive every action, Actions Inreturn someOtherAction(); // return an action, Actions Out}))
}

It is important to understand that all the actions received in the Epic have already finished running through the reducers.

重要的是要了解Epic中收到的所有动作都已经通过减速器完成了

Inside an Epic, we can use any RxJS observable patterns, and this is what makes redux-observables useful.

在Epic内部,我们可以使用任何RxJS可观察模式,这就是使redux-observable有用的原因。

Example: we can use the .filter operator to create a new intermediate observable. Similarly, we can create any number of intermediate observables, but the final output of the final observable must be an action, otherwise an exception will be raised by redux-observable.

示例:我们可以使用.filter运算符创建一个新的中间可观察对象。 同样,我们可以创建任意数量的中间可观察变量,但是最终可观察变量的最终输出必须是一个动作,否则redux-observable会引发异常。

const sampleEpic = (action$, state$) => {return action$.pipe(filter(action => action.payload.age >= 18), // can create intermediate observables and streamsmap(value => above18(value)) // where above18 is an action creator);
}

Every action emitted by the Epics are immediately dispatched using the store.dispatch().

Epics发出的每个动作都会立即使用store.dispatch()调度。

建立 (Setup)

First, let's install the dependencies.

首先,让我们安装依赖项。

npm install --save rxjs redux-observable

Create a separate folder named epics to keep all the epics. Create a new file index.js inside the epics folder and combine all the epics using the combineEpics function to create the root epic. Then export the root epic.

创建一个名为epics的单独文件夹以保留所有史诗。 创建一个新的文件index.js里面的epics文件夹,然后将所有使用的史诗combineEpics函数来创建根史诗。 然后导出根史诗。

import { combineEpics } from 'redux-observable';
import { epic1 } from './epic1';
import { epic2 } from './epic2';const epic1 = (action$, state$) => {...   
}const epic2 = (action$, state$) => {...   
}export default combineEpics(epic1, epic2);

Create an epic middleware using the createEpicMiddleware function and pass it to the createStore Redux function.

使用createEpicMiddleware函数创建一个史诗中间件,并将其传递给createStore Redux函数。

import { createEpicMiddleware } from 'redux-observable';
import { createStore, applyMiddleware } from 'redux';
import rootEpic from './rootEpics';const epicMiddleware = createEpicMiddlware();const store = createStore(rootReducer,applyMiddleware(epicMiddlware)
);

Finally, pass the root epic to epic middleware's .run method.

最后,将根史诗传递给史诗中间件的.run方法。

epicMiddleware.run(rootEpic);

一些实际用例 (Some Practical Usecases)

RxJS has a big learning curve, and the redux-observable setup worsens the already painful Redux setup process. All that makes Redux observable look like an overkill. But here are some practical use cases that can change your mind.

RxJS的学习曲线很大,可观察到的Redux设置会使本来就很痛苦的Redux设置过程恶化。 所有使Redux都可观察到的东西看起来像是一个过大的杀伤力。 但是这里有一些可以改变主意的实际用例。

Throughout this section, I will be comparing redux-observables with redux-thunk to show how redux-observables can be helpful in complex use-cases. I don't hate redux-thunk, I love it, and I use it every day!

在本节中,我将比较redux-observables和redux-thunk,以显示redux-observables如何在复杂的用例中提供帮助。 我不讨厌redux-thunk,我喜欢它,并且每天都在使用它!

1.进行API调用 (1. Make API Calls)

Usecase: Make an API call to fetch comments of a post. Show loaders when the API call is in progress and also handle API errors.

用例:进行API调用以获取帖子的评论。 在API调用正在进行时显示加载程序,并处理API错误。

A redux-thunk implementation will look like this,

一个redux-thunk实现将如下所示,

function getComments(postId){return (dispatch) => {dispatch(getCommentsInProgress());axios.get(`/v1/api/posts/${postId}/comments`).then(response => {dispatch(getCommentsSuccess(response.data.comments));}).catch(() => {dispatch(getCommentsFailed());});}
}

and this is absolutely correct. But the action creator is bloated.

这是绝对正确的。 但是动作创造者is肿。

We can write an Epic to implement the same using redux-observables.

我们可以编写一个Epic以使用redux-observables实现相同的功能。

const getCommentsEpic = (action$, state$) => action$.pipe(ofType('GET_COMMENTS'),mergeMap((action) => from(axios.get(`/v1/api/posts/${action.payload.postId}/comments`).pipe(map(response => getCommentsSuccess(response.data.comments)),catchError(() => getCommentsFailed()),startWith(getCommentsInProgress()))
);

Now it allows us to have a clean and simple action creator like this,

现在,它使我们可以像这样一个干净整洁的动作创建者,

function getComments(postId) {return {type: 'GET_COMMENTS',payload: {postId}}
}

2.请求退信 (2. Request Debouncing)

Usecase: Provide autocompletion for a text field by calling an API whenever the value of the text field changes. API call should be made 1 second after the user has stopped typing.

用例:每当文本字段的值更改时,通过调用API来为文本字段提供自动补全功能。 用户停止输入后应在1秒钟内进行API调用。

A redux-thunk implementation will look like this,

一个redux-thunk实现将如下所示,

let timeout;function valueChanged(value) {return dispatch => {dispatch(loadSuggestionsInProgress());dispatch({type: 'VALUE_CHANGED',payload: {value}});// If changed again within 1 second, cancel the timeouttimeout && clearTimeout(timeout);// Make API Call after 1 secondtimeout = setTimeout(() => {axios.get(`/suggestions?q=${value}`).then(response =>dispatch(loadSuggestionsSuccess(response.data.suggestions))).catch(() => dispatch(loadSuggestionsFailed()))}, 1000, value);}
}

It requires a global variable timeout. When we start using global variables, our action creators are not longer pure functions. It also becomes difficult to unit test the action creators that use a global variable.

它需要全局变量timeout 。 当我们开始使用全局变量时,动作创建者不再是纯函数。 对使用全局变量的动作创建者进行单元测试也变得困难。

We can implement the same with redux-observable using the .debounce operator.

我们可以使用.debounce运算符,通过redux-observable实现相同的.debounce

const loadSuggestionsEpic = (action$, state$) => action$.pipe(ofType('VALUE_CHANGED'),debounce(1000),mergeMap(action => from(axios.get(`/suggestions?q=${action.payload.value}`)).pipe(map(response => loadSuggestionsSuccess(response.data.suggestions)),catchError(() => loadSuggestionsFailed()))),startWith(loadSuggestionsInProgress())
);

Now, our action creators can be cleaned up, and more importantly, they can be pure functions again.

现在,我们的动作创建者可以被清理,更重要的是,他们可以再次成为纯函数。

function valueChanged(value) {return {type: 'VALUE_CHANGED',payload: {value}}
}

3.要求取消 (3. Request Cancellation)

Usecase: Continuing the previous use-case, assume that the user didn't type anything for 1 second, and we made our 1st API call to fetch the suggestions.

用例:继续前面的用例,假设用户在1秒钟内未键入任何内容,并且我们进行了第1次API调用以获取建议。

Let's say the API itself takes an average of 2-3 seconds to return the result. Now, if the user types something while the 1st API call is in progress, after 1 second, we will make our 2nd API. We can end up having two API calls at the same time, and it can create a race condition.

假设API本身平均需要2-3秒才能返回结果。 现在,如果用户在进行第一个API调用时键入了一些内容,则在1秒钟后,我们将创建第二个API。 我们最终可能同时拥有两个API调用,并且它可以创建竞争条件。

To avoid this, we need to cancel the 1st API call before making the 2nd API call.

为避免这种情况,我们需要先取消第一个API调用,然后再进行第二个API调用。

A redux-thunk implementation will look like this,

一个redux-thunk实现将如下所示,

let timeout;
var cancelToken = axios.cancelToken;
let apiCall;function valueChanged(value) {    return dispatch => {dispatch(loadSuggestionsInProgress());dispatch({type: 'VALUE_CHANGED',payload: {value}});// If changed again within 1 second, cancel the timeouttimeout && clearTimeout(timeout);// Make API Call after 1 secondtimeout = setTimeout(() => {// Cancel the existing APIapiCall && apiCall.cancel('Operation cancelled');// Generate a new tokenapiCall = cancelToken.source();axios.get(`/suggestions?q=${value}`, {cancelToken: apiCall.token}).then(response => dispatch(loadSuggestionsSuccess(response.data.suggestions))).catch(() => dispatch(loadSuggestionsFailed()))}, 1000, value);}
}

Now, it requires another global variable to store the Axios's cancel token. More global variables = more impure functions!

现在,它需要另一个全局变量来存储Axios的cancel令牌。 更多全局变量=更多不纯函数!

To implement the same using redux-observable, all we need to do is replace .mergeMap with .switchMap.

为了使用redux-observable实现相同的功能,我们要做的就是将.mergeMap替换为.switchMap

const loadSuggestionsEpic = (action$, state$) => action$.pipe(ofType('VALUE_CHANGED'),throttle(1000),switchMap(action => from(axios.get(`/suggestions?q=${action.payload.value}`)).pipe(map(response => loadSuggestionsSuccess(response.data.suggestions)),catchError(() => loadSuggestionsFailed()))),startWith(loadSuggestionsInProgress())
);

Since it doesn't require any changes to our action creators, they can continue to be pure functions.

由于不需要对我们的动作创建者进行任何更改,因此他们可以继续成为纯函数。

Similarly, there are many use-cases where Redux-Observables actually shines! For example, polling an API, showing snack bars, managing WebSocket connections, etc.

同样,在很多用例中,Redux-Observables确实发光! 例如,轮询API,显示小吃店, 管理WebSocket连接等。

结论 (To Conclude)

If you are developing a Redux application that involves such complex use-cases, it is highly recommended to use Redux-Observables. After all, the benefits of using it are directly proportional to the complexity of your application, and it is evident from the above mentioned practical use-cases.

如果您正在开发涉及此类复杂用例的Redux应用程序,则强烈建议使用Redux-Observables。 毕竟,使用它的好处与您的应用程序的复杂度成正比,并且从上述实际用例中可以明显看出。

I strongly believe using the right set of libraries will help us to develop much cleaner and maintainable applications, and in the long term, the benefits of using them will outweigh the drawbacks.

我坚信使用正确的库集将帮助我们开发更清洁和可维护的应用程序 ,从长远来看,使用它们的好处将超过弊端。

翻译自: https://www.freecodecamp.org/news/beginners-guide-to-rxjs-redux-observables/

redux rxjs

相关文章:

javascript数组排序和prototype详解

原型的概念:&#xff1a;原型对象里的所有属性和方法 被所有构造函数实例化出来的对象所共享&#xff0c;类似于java中的 static 正因为共享所以单一的操作 就会影响了全局&#xff0c;因此使用时需注意 基于prototype&#xff1a;为数组扩展方法 //获取数组最大值function get…

Qt 在Label上面绘制罗盘

自己写的一个小小的电子罗盘的一个小程序&#xff0c;不过是项目的一部分&#xff0c;只可以贴绘制部分代码 效果如下图 首先开始自己写的时候&#xff0c;虽然知道Qt 的坐标系是从左上角开始的&#xff0c;所以&#xff0c;使用了算法&#xff0c;在绘制后&#xff0c;在移动回…

终极方案!解决正确设置LaunchImage后仍然不显示的问题

对于如何设置LaunchImage&#xff0c;网络上有各种各样的教程。 主要分2点&#xff1a; 1. 正确设置图片尺寸 2. 取消LaunchScreen.xib 但是经过上述步骤之后&#xff0c;你觉得完全没有问题了&#xff0c;但是仍然无法显示LaunchImage。 或者&#xff0c;你在多个模拟器上…

c# 持续集成 单元测试_如何在不进行单元测试的情况下设置持续集成

c# 持续集成 单元测试Do you think continuous integration is not for you because you have no automated tests? Or no unit tests at all? Not true. Tests are important. But there are many more aspects to continuous integration than just testing. Lets see what…

Handlebars模板引擎

介绍 Handlebars 是 JavaScript 一个语义模板库&#xff0c;通过对view和data的分离来快速构建Web模板。它采用"Logic-less template"&#xff08;无逻辑模版&#xff09;的思路&#xff0c;在加载时被预编译&#xff0c;而不是到了客户端执行到代码时再去编译&#…

字符集图标制作

字符集图标&#xff1a; 将网页上常见的icon做成font&#xff08;字符集&#xff09;&#xff0c;以字体的方式插入到网页上&#xff0c;作用是减轻服务器负担&#xff0c;减少宽带。 我最常在这两个网站上下载字体图标&#xff1a; https://icomoon.io/app/#/select https://w…

Adobe源码泄漏?3行代码搞定,Flash动画无缝导入Android/iOS/cocos2dx(一)

[注] iOS代码已重构&#xff0c;效率提升90%&#xff0c;200层动画不卡。[2016.10.27] 项目介绍 项目名称&#xff1a;FlashAnimationToMobile 源码。 使用方法点这里。 这是一个把flash中的关键帧动画(不是序列帧)导出&#xff0c;然后在iOS&#xff0f;Android原生应用中解…

背景图像位置css_CSS背景图像大小教程–如何对整页背景图像进行编码

背景图像位置cssThis tutorial will show you a simple way to code a full page background image using CSS. And youll also learn how to make that image responsive to your users screen size.本教程将向您展示一种使用CSS编写整页背景图像的简单方法。 您还将学习如何使…

复习es6-解构赋值+字符串的扩展

1. 数组的解构赋值 从数组中获得变量的值&#xff0c;给对应的声明变量赋值,&#xff0c;有次序和对应位置赋值 解构赋值的时候右边必须可以遍历 解构赋值可以使用默认值 惰性求值&#xff0c;当赋值时候为undefined时候&#xff0c;默认是个函数就会执行函数 2.对象解构赋值 与…

Adobe源码泄漏?3行代码搞定,Flash动画无缝导入Android/iOS/cocos2dx(二)

[注] iOS代码已重构&#xff0c;效率提升90%&#xff0c;200层动画不卡。[2016.10.27] 上一篇 点此阅读 简要介绍了FlashToAnimation的功能&#xff0c;也就是将flash动画无缝导入到Android/iOS及cocos2dx中运行, 这一篇介绍这个库的使用方法。点此查看源码。 准备工作 首先…

the user operation is waiting for building workspace to complete解决办法

如果你在开发android应用程序中总是出现一个提示&#xff0c;显示“the user operation is waiting for "building workspace" to complete”&#xff0c;解决办法如下&#xff1a; 1.选择菜单栏的“Project”,然后把菜单栏中“Build Automatically”前面的对钩去掉。…

ios开发趋势_2020年将成为iOS应用开发的主要趋势

ios开发趋势Technology has always brought something new with time. And with these ever-changing technologies, you need to stay updated to get all the benefits from whats new. 随着时间的流逝&#xff0c;技术总是带来新的东西。 借助这些不断变化的技术&#xff0c…

http 权威指南 目录

第一部分 HTTP&#xff1a;Web的基础 第1章 HTTP概述 1.1 HTTP——因特网的多媒体信使 1.2 Web客户端和服务器 1.3 资源 1.3.1 媒体类型 1.3.2 URI 1.3.3 URL 1.3.4 URN 1.4 事务 1.4.1 方法 1.4.2 状态码 1.4.3 Web页面中可以包含多个对象 1.5 报文 1.6 连接 1.6.1 TCP/IP 1.6…

java初学者笔记总结day9

异常的概念throwable&#xff1a;异常&#xff0c;程序非正常执行的情况error&#xff1a;错误&#xff0c;程序非正常执行的情况&#xff0c;这种问题不能处理&#xff0c;或不应该处理exception&#xff1a;例外&#xff0c;程序非正常执行的情况&#xff0c;这种问题可以通过…

1小时学会:最简单的iOS直播推流(一)介绍

最简单的iOS 推流代码&#xff0c;视频捕获&#xff0c;软编码(faac&#xff0c;x264)&#xff0c;硬编码&#xff08;aac&#xff0c;h264&#xff09;&#xff0c;美颜&#xff0c;flv编码&#xff0c;rtmp协议&#xff0c;陆续更新代码解析&#xff0c;你想学的知识这里都有…

leetcode dfs_深度优先搜索:具有6个Leetcode示例的DFS图遍历指南

leetcode dfsHave you ever solved a real-life maze? The approach that most of us take while solving a maze is that we follow a path until we reach a dead end, and then backtrack and retrace our steps to find another possible path. 您是否解决了现实生活中的迷…

MySQL排序原理与MySQL5.6案例分析【转】

本文来自&#xff1a;http://www.cnblogs.com/cchust/p/5304594.html&#xff0c;其中对于自己觉得是重点的加了标记&#xff0c;方便自己查阅。更多详细的说明可以看沃趣科技的文章说明。 前言 排序是数据库中的一个基本功能&#xff0c;MySQL也不例外。用户通过Order by…

7.RabbitMQ RFC同步调用

RabbitMQ RFC同步调用是使用了两个异步调用完成的&#xff0c;生产者调用消费者的同时&#xff0c;自己也作为消费者等待某一队列的返回消息&#xff0c;消费者接受到生产者的消息同时&#xff0c;也作为消息发送者发送一消息给生产者。参考下图&#xff1a; 调用流程如下&…

1小时学会:最简单的iOS直播推流(二)代码架构概述

最简单的iOS 推流代码&#xff0c;视频捕获&#xff0c;软编码(faac&#xff0c;x264)&#xff0c;硬编码&#xff08;aac&#xff0c;h264&#xff09;&#xff0c;美颜&#xff0c;flv编码&#xff0c;rtmp协议&#xff0c;陆续更新代码解析&#xff0c;你想学的知识这里都有…

sh脚本每天创建一个文件夹_我每天创建一个月的视频。 这就是发生的事

sh脚本每天创建一个文件夹At the end of 2019 I promised that 2020 would be all about my YouTube channel. So thats what Ive been doing. &#x1f603; 在2019年底&#xff0c;我保证2020年将成为我的YouTube频道的全部 。 这就是我一直在做的。 &#x1f603; On the f…

1小时学会:最简单的iOS直播推流(三)使用系统接口捕获音视频数据

最简单的iOS 推流代码&#xff0c;视频捕获&#xff0c;软编码(faac&#xff0c;x264)&#xff0c;硬编码&#xff08;aac&#xff0c;h264&#xff09;&#xff0c;美颜&#xff0c;flv编码&#xff0c;rtmp协议&#xff0c;陆续更新代码解析&#xff0c;你想学的知识这里都有…

什么是bower

Bower是一个客户端技术的软件包管理器&#xff0c;它可用于搜索、安装和卸载如JavaScript、HTML、CSS之类的网络资源。其他一些建立在Bower基础之上的开发工具&#xff0c;如YeoMan和Grunt&#xff0c;这个会在以后的文章中介绍。 准备工作 安装node环境:node.js安装Git&#x…

ES6中export及export default的区别

在ES6中&#xff0c;export和export default均可用于导出常量、函数、文件、模块等&#xff0c;你可以在其他文件或模块中通过import (常量 | 函数 | 文件 | 模块)名的方式将其导入&#xff0c;以便能够对其进行使用&#xff0c;但在一个文件或模块中&#xff0c;export、impo…

sm2加密算法实例_实例说明加密算法

sm2加密算法实例Cryptography, at its most basic, is the science of using codes and ciphers to protect messages. 密码学从根本上讲就是使用代码和密码保护消息的科学。 Encryption is encoding messages with the intent of only allowing the intended recipient to un…

git---远程仓库版本回滚

开发中&#xff0c;发现有错误版本提交带远程分支master&#xff0c;怎么处理&#xff1f; 1 简介 最近在使用git时遇到了远程分支需要版本回滚的情况&#xff0c;于是做了一下研究&#xff0c;写下这篇博客。 2 问题 如果提交了一个错误的版本&#xff0c;怎么回退版本&#x…

1小时学会:最简单的iOS直播推流(四)如何使用GPUImage,如何美颜

最简单的iOS 推流代码&#xff0c;视频捕获&#xff0c;软编码(faac&#xff0c;x264)&#xff0c;硬编码&#xff08;aac&#xff0c;h264&#xff09;&#xff0c;美颜&#xff0c;flv编码&#xff0c;rtmp协议&#xff0c;陆续更新代码解析&#xff0c;你想学的知识这里都有…

团队任务四(无图)

任务要求&#xff1a; WBS练习对团队项目进行任务分解要求所有人共同参与队长列出需求成员进行估计队长领导大家达成共识形成团队报告&#xff0c;发至团队博客项目分解&#xff1a; 一、手机监控(24h) &#xff08;1&#xff09;手机当前运行程序监控&#xff08;用以观察用户…

react测试组件_测试驱动的开发,功能和React组件

react测试组件This article is part of my studies on how to build sustainable and consistent software. In this post, we will talk about the thinking behind the testing driven development and how to apply this knowledge to simple functions, web accessibility,…

CDOJ 1073 线段树 单点更新+区间查询 水题

H - 秋实大哥与线段树Time Limit:1000MS Memory Limit:65535KB 64bit IO Format:%lld & %llu Submit Status Practice UESTC 1073Appoint description: System Crawler (2016-04-24)Description “学习本无底&#xff0c;前进莫徬徨。” 秋实大哥对一旁玩手机的学…

1小时学会:最简单的iOS直播推流(五)yuv、pcm数据的介绍和获取

最简单的iOS 推流代码&#xff0c;视频捕获&#xff0c;软编码(faac&#xff0c;x264)&#xff0c;硬编码&#xff08;aac&#xff0c;h264&#xff09;&#xff0c;美颜&#xff0c;flv编码&#xff0c;rtmp协议&#xff0c;陆续更新代码解析&#xff0c;你想学的知识这里都有…