6

尝试新的架构范式,其中演示者创建不可变状态(模型)流并查看只是呈现它。

无法理解如何处理我们只需要一次性制作一些事件的情况。有几个例子。

1)笔记应用程序。我们有editTextsaveButton。用户点击saveButton,一些处理发生,editText 应该被清除。你们能描述一下我们ViewState这里的内容和大致的逻辑流程吗?

我现在看到的问题和陷阱:

  1. 在presenter订阅editText.textChanges()。如果我们textViewState每次渲染调用中都有并渲染它,那么我们将陷入递归,因为它将发出新的 textChange 并更新状态并再次渲染。
  2. 我们是否需要textViewState方向文本或进程终止/恢复上恢复它,看起来它在这里开箱即用。但是想象一下recyclerView滚动位置。我们绝对需要保存它才能恢复。我们无法在每次渲染调用时恢复它,因为它看起来很奇怪,不是吗?
  3. 如果我们将这样的逻辑视为副作用并称.doOnNext{ view.clearText() }其是有道理的,但我们是否参考了规范 MVI 实现中的视图?正如我所见,莫斯比没有它。
  4. 这是有道理的,但是在doOnNext呼叫的那一刻,视图可能会死掉。MVI 应该帮助我们解决这个问题,但前提是它是 的一部分ViewState,对吧?

2) Github 应用程序。第一个屏幕(组织):orgEditText, okButton, progressBar. 第二个屏幕(回购)recyclerView:。当用户进入组织orgEditText并单击okButton应用程序时,应向 API 发出请求,并在成功时导航到 Repos 屏幕,或在失败时显示 toast。您能否再次描述ViewState一下 Org 屏幕以及应该是什么样的逻辑?

我现在看到的问题和陷阱:

  1. 我们应该在加载时显示progressBar和禁用。okButton我们应该喜欢加载/内容/错误密封类(让我们调用它ContentState)并将其实例放在我们的ViewState. View 知道如何渲染ContentState.loading、显示progressBar和禁用okButton. 我对吗?
  2. 那么如何处理导航呢?与 1.3 和 1.4 相同的问题。
  3. 我已经看到导航应该被视为副作用的意见,但同样是 1.4。
  4. 吐司 - 状态是否存在或者我们认为这是副作用?同样的问题。

谷歌提出SingleLiveEvent了解决方案,但它看起来很奇怪,然后应该有尽可能多的LiveData<SingleLiveEvent>流,而不是真正的单一事实来源。其他人建议从渲染函数生成的新意图更好,但有可能一些异步操作会再次改变状态,我们将在第一个显示时获得第二个 Toast,依此类推。

4

1 回答 1

7

text1)Notes App:在一个完美的世界中:是的,只要用户插入文本并呈现,您的 ViewState 就会发生变化。关于递归:我可能错了,但我认为某处的 RxBindings 提供了一个 Observable,它不仅包含更改的文本,而且如果此更改是由用户输入或以编程方式设置文本引起的,则还包含一个布尔标志。if (editText.text != viewState.text)无论如何,如果您检查并仅设置文本以防它们不同,我认为您也可以解决递归问题(请记住,您可能必须使用在文本更改后触发的 TextWatcher 回调来启动意图,不是“之前将要改变”)。

话虽如此,在 Android 上,我们并不是生活在一个完美的世界中。正如您已经说过的,文本将由 android 自动恢复。因此,不将文本作为 ViewState 的一部分是有意义的。

所以听起来在这种情况下 ViewState 只是一个像这样的枚举:

enum ViewState {
   // The user can type typing text
   IDLING,

   // The app is saving the note
   PROCESSING,

   // After having saved (PROCESSING) the note, CLEARED means, show a new empty note  
   CLEARED
}

所以初始状态是IDLING。然后,一旦应该保存注释,下一个发出的 ViewState 就是PROCESSING. 一旦成功,您的业务逻辑会立即触发 a CLEAREDIDLING因此最后用户会再次看到一个空便笺并可以开始输入新便笺。

不要doOnNext()用于操作视图。ViewState 是视图的唯一真实来源。

关于 RecyclerView:如果没有,RecyclerView 会自动恢复它的滚动位置(在状态恢复后,您将 LayoutManager 和/或适配器设置为延迟)。不过,如果您想在 ViewState 中对滚动位置进行建模,这在完美的世界中将是我猜想的最佳解决方案,您应该考虑不要在每个滚动像素上更新 ViewState 中的滚动位置,而是在用户滚动后执行不再滚动/投掷已完成。

2) Github 应用程序:

  1. 我们应该在加载时显示 progressBar 并禁用 okButton。我们应该有像加载/内容/错误密封类(让我们称之为 ContentState),并在我们的 ViewState 中有它的实例。View 知道如何渲染 ContentState.loading 并显示 progressBar 并禁用 okButton。我对吗?

是的

  1. 那么如何处理导航呢?

对我来说,将其作为副作用处理效果很好:我有一个类Navigator被注入到演示者中并用于doOnNext { navigator.goToX() }. 然后,导航器将其分派给可以临时附加/分离的另一个组件。所以这个其他组件正在观察“导航事件”的导航器我这样做的原因是这个组件没有泄漏活动/片段上下文。“这个组件”可以直接是 Activity 或 Fragment 或其他任何东西,但我倾向于有一个专用的类,让我们称它为Router观察Navigator导航事件并执行 FragmentTransactions您在应用程序中使用的操作或任何操作。

  1. 吐司 - 状态是否存在或者我们认为这是副作用?同样的问题。

这可以像你可以做的一样处理Snackbar(见这里)。Toast 没有隐藏 Toast 的 API。因此,您可以一个接一个地立即触发两个 ViewState,而不是计时器:第一个设置了错误标志(然后导致 Toast 确实显示在屏幕上),第二个您“清除”此标志。像这样的东西:

Observable.just( ViewState(error = true, ...), new ViewState( error = false, ... )

我希望澄清一些事情,但一如既往:不要把它们当作灵丹妙药。做最适合您的应用程序和用例的事情。不要过于虔诚,这始终是逐案决定的。

于 2017-12-13T12:41:08.437 回答