Day 21 | 状态管理套件 MobX - 到底什麽是状态管理

状态管理?

在介绍 MobX 以前我想先来说一下什麽是「状态管理」

究竟为什麽我们需要「状态管理」,还记得前几篇想到 Flutter其中一个概念 UI = F(State) 吗,其实这个概念是借鉴 React 的设计概念。我们所看的画面都是因为状态经过某些function後形成的,但为什麽要这样?

如果是写过 JQuery 的读者应该有体验过要自己手动更新画面的痛苦,如果没有特别额外写什麽框架的话 JQuery 就是我们需要在 dom上绑定我们的互动事件,然後在这个事件里执行更新值之後再从这个事件去选择我们要更新的dom并把值注入给它,上述一切都是要自己手动撰写的。

而到了之後的 reactive 式的前端(不论是网页或者App或者哪个框架)大多数时候我们是将UI绑定状态及事件,并在事件里去实作更新状态,当状态更新後会自动更新UI。

我个人是认为这种方案对开发者比较友善,毕竟按照目前前端的趋势来说互动会愈来愈复杂,如果这类事情还要自己手动做也未免太累人。但也因为是自动更新UI所以必定会有不必要的re-render以及生命周期相关的事情要处理,但这系列文章应该不会说到这边去。

说回 Flutter ,在 Flutter 里内建的状态管理是使用 setState 这个东西,它是一个叫做 Stateabstract class 下的 method。它调用後会触发 build() 进行重新渲染。
使用上很简单我就在 extends Stateclass 里宣告几个mutable的值,在预设范例里是 _MyHomePageState ,然後将这些值放到build()的widget里,如果要让值变更时就是使用 setState 来进行值的更新。

那为什麽我们需要其他方法来帮我们管理状态,我自己认为至少有这几个理由

  1. UI与逻辑耦合
  2. 跨组件共用state
  3. 要传递到深层widget时会很麻烦

先说在前面这几个问题就算不套用任何框架、套件就也能靠着原生方法来解决,但是就是没那麽简单使用或者就程序码长了点丑了点而已。

UI与逻辑耦合

你可能会发现我们用了 setState 後我们得在UI层(先不讨论到底要如何划分范畴下总之就是指我们撰写UI的那个class里)宣告一堆function,更何况在实际开发场景中还要再加上後端资料的相关问题。
但这真的是必须在UI层里实作的事情吗?难道没有一些办法可以将setState 相关的事情拿出去别的地方做,然後我的UI层只要等着接收资料就好了吗?

跨组件共用state

今天如果都只是单向的传递从最上面往下传,其实都还算好理解,但如果是又两个子widget其中一边要根据另外一边事件进行更新,那又该怎麽办?

传递到深层widget

依目前范例来说的确是没办法显示这个问题点,毕竟说真的也不怎麽复杂所以也没有额外切分widget,但是因为flutter 的 aggressive composability 的关系会让我们每个widget的巢状结构会变得十分地深更加导致我们切更层的widget。这也会导致我们要将状态/事件向下传的层数变得更多,而这之间每一层的widget我们都要在他的constructor 加上这个 state ,并且每个也都要传入,widget层数一多起来实在是令人烦躁。

那我们要如何解决的这几个问题?

以「UI与逻辑耦合」来说,我们就是将各个区块职责分离乾净,谁该做什麽事情谁只能做什麽事情从一开始就划分清楚。

依我自己在 react 开发经验来说,在没有redux(一个状态管理套件)的情况下我会尽量让状态统一在一个 component 里宣告,也包含了fetch api data等等跟资料相关的事情。之後再写各种 function 将 setState (react hook的setState跟flutter不一样)包装起来往下传到子component 的props。但为什麽不是直接传setState 而是要帮装一层,其中一个原因就是

我不想让子 component 里可以任意的改变状态,而是要按照我上面规定好的逻辑进行变更,也就是我不想把state更新逻辑写在子 componet 我希望愈下层的component只要做好纯粹显示这个功能就好。

某种程度上他已经部分解决了「UI与逻辑耦合」,但实际上只是藉由将state上升统一管理,但这段更新逻辑还是存在在component里。

所以更进一步的话就是使用了 redux 来将逻辑完全抽离出去,而且我只要在子component这里select及dispatch就好,根本不用在上层component额外宣告那些handler再传下来。

听起来好像顺便解决了「传递到深层widget」及「跨组件共用state」但这是react-redux的useSelector及useDispatch顺便解决的问题,否则你还是要将外部的那个东西(其实就是store)一层一层传下去再取出来。

而在 Flutter 中有很多套件可以解决「传递到深层widget」以及「跨组件共用state」,像是provider、riverpod、getx等等很多很多套件可以做到这些事情。我们可以套过这些套件藉由一些方法帮我们统整状态甚至其他东西放到最上层,而当我们需要时再取出来。(没错这听起来很像依赖注入)

MobX

所以接下来我们将会使用 MobX 这个套件来进行状态管理,我自己的观点是 MobX 是一个「纯粹的状态管理套件」,其实他没办法帮我们解决「传递到深层widget」以及「跨组件共用state」,那为什麽要用它而不是用其他的套件呢?最主要的原因就是它够简单,对於初学者的心智负担较小。

MobX 最主要的功能是帮我们简单的连接状态与UI,而其中是靠着 observer pattern 来达成这件事情。observer pattern 简单来说是观察者(observer)更新後会自动通知观察者(observables)

MobX整个流程图会像是这样:

https://ithelp.ithome.com.tw/upload/images/20211004/20112906JbcX7rdTIS.png

有点像是简单版的redux,我们所有的状态变更都是藉由 Action 来更新之後会更新状态也就是(observable) 然後如果有 computed 会再计算一层之後再触发更新(也就是叫observer更新)

在MobX中我们可以将这一切包进一个 class 里然後将逻辑都写在那边,那我的UI就只剩从这个class 取资料以及action了。

通常这个 class 就会被称为 store 之类的称呼,所以如果我要跨组件共用这个 store 我就是要藉由其他套件将这个 store 传到其他的子widge,而不是直接将状态传出去。


这篇文写起来有点无聊但感觉也是在讲状态管理套件前必须要说明的内容。

虽然 MobX 也是从 react 过来的套件,但其实我在 react 里完全没有使用过 MobX 原因是 redux 在社群的声量远大於MobX,而且在目前react hook的环境里感觉 redux 会比较搭,但也因为 Flutter 的并没有 hook 所以我在使用 redux 会觉得卡卡的。
(我知道有hooks_widget,我使用过但感觉还是不太像 react hook那样。)

但我觉得如果我不是两边都有在开发应该也不会有这种卡卡的感觉,最主要是现在 redux-toolkit让我在react中使用redux的开发体验实在是太舒服了。

参考资料

https://mobx.netlify.app/


<<:  Unity与Photon的新手相遇旅途 | Day19-Photon基本设定

>>:  [Android Studio 30天自我挑战] 透过Banner来轮播广告资讯

Dialog 关闭後更新 Grid 资料 / 显示储存的图档 - day20

目标 承前篇 当学生资料修改或上传图档後,能够在 Grid 即时更新修正後的资料,并於点选学生展开显...

[Day30] Room的坑只好自己补

Caused by: java.lang.IllegalStateException: Canno...

[NestJS 带你飞!] DAY06 - Provider (上)

前一篇有提到 Provider 与 Module 之间有很核心的机制,该机制使用了 依赖注入 的概念...

Day 30: 遗漏的章节

「目前为止,所有建议无疑将帮助你设计出更好的软件,这些软件是由具有明确边界、职责、依赖关系受控的元...

Day 21 - 天眼CNN 的耳朵和嘴巴 - RNN(2) -LSTM

LSTM vs. Simple RNN 再看一次Simple RNN 图中说明 绿色框为一个cell...