NgRx:监听服务中的状态变化而不是使用 Effect

NgRx: Listening to state change in service rather than using an Effect

我的问题是:服务可以侦听状态变化并根据状态值进行 http 调用,还是必须由效果 (NgRx) 触发? 因此,不是像您通常在 Web 上找到的示例那样通过效果调用服务,而是让服务监听商店的一部分并根据它做出反应。因此没有更多的效果。 我的问题更像是一个哲学(最佳实践)问题而不是技术问题,因为从技术上讲,两者都有效。

更新

这里有一个具体的案例来理解我为什么要问:

上下文:

我有一个从后端收到的元素列表。我可以过滤该元素列表。一次可以有多个过滤器。过滤在后端完成。

当前实施:

组件调度 FILTER_ELEMENTS 操作。该操作由调用 http 服务的效果拾取。该服务向后端询问基于过滤器的元素列表。 因为可以有多个过滤器,效果(或服务)需要知道当前的过滤器是什么并将新过滤器添加到该列表中。所以我在效果中添加了一个 .withLatestFrom(this.state$) 并从那里获取当前的过滤器列表。

问题:

问题是状态没有用新过滤器更新。我可以在我的减速器中捕获 FILTER_ELEMENTS 操作并更新状态,但这会导致其他几个潜在问题。 effect 和 reducer 哪个会先被调用? 后台出错怎么办?我的状态已使用新过滤器更新,但列表未被过滤。 asynchornism 呢?如果我在服务器响应之前或状态更新之前要求另一个过滤器怎么办?

我觉得这有点太乱了。

所以我的问题是: 那商店真的是真实的唯一来源吗?而不仅仅是 "log" 发生的事情可能是最新的,也可能不是最新的。我的意思是想要添加过滤器的组件可以通过调度 ADD_FILTER 操作来更新状态中的过滤器列表。我的服务可以监听过滤器列表并在每次列表更改时调用后端。 因此,该服务仅在状态发生变化时调用后端,并且我们知道的是最新的真实状态。然后使用更新后的列表向商店发送 "success" 操作。 我看不出这会导致比效果更多的潜在无限循环……毕竟,效果也会导致无限循环…… 所以我的服务实际上是一个监听存储变化的效果,而不是直接监听动作。

您不应该这样做,因为它明显违反了单向数据流。异步事件导致操作去更新存储,从存储中出来的数据不会触发新的异步事件,而新的异步事件又会触发新的操作。这会产生严重错误的可能性,例如无限事件循环,并且还会使您的应用程序难以推理。效果服务就是出于这个原因而存在的,以确保单向数据流不会被违反。

重要的是要记住 ngrx 一个动作代表整个应用程序状态的变化,句号。效果服务本质上采取了一个需要一些远程资源来描述完整状态更改的操作,并获取该远程资源并将其映射到一个新操作中,该操作在它到达商店之前确实代表了完整的状态更改。如果您根据商店中出现的内容进行进一步的状态更改,那么很明显您没有在您的操作中完整描述您的状态更改。

重播实际进入商店的操作应该每次都产生相同的状态,同样,如果您根据从商店中获得的内容进行其他状态更改,情况就不会如此。

编辑以解决其他问题:

商店是设计的唯一真实来源,IE,应用程序的状态不通过商店就不会更新。作为开发人员,您有责任强制执行此操作。它不应该是你追溯更新它以反映 "what happened",它应该是实际的变化,商店正在存储你的应用程序的状态。

就目前而言,您似乎想要过滤元素,然后通过调度 UPDATE_FILTERS 操作对接收这些元素做出反应,这是 100% 错误的做法。

解决问题的第一步是确保您的操作包含状态更改所需的所有信息,因此,如果您的效果服务需要访问当前状态,则说明您做错了什么。 FILTER_ELEMENTS 操作应包含所有需要的过滤器,效果服务不应访问当前状态。

下一步是再次确保您的操作包含状态更改所需的所有信息。不要在你的 reducer 中捕获 FITLER_ELEMENTS,映射到一个新的动作,ELEMENTS_FITLERED,它包含过滤的元素和过滤器,并对它做出反应,并在同一个动作中应用两个状态变化。在出现错误的情况下,您可以捕获它并使用创建该状态所需的数据映射到 FILTER_ERROR 操作,其中可能包括旧过滤器。至于在旧过滤器完成之前通过的新过滤器,那么您可以使用 switchMap 来确保查看最新的过滤器并忽略以前的请求。

所有这些都可以在框架中解决,您的效果服务不应该导致无限循环,如果可以,则您设计错误,可能是通过尝试访问效果服务中的状态。但是你永远不应该用新的动作来回应动作。