大家好,我是Kason。在我们React进阶源码群里,除了React,状态管理是讨论频率最高的话题。奇怪的是,群里很多朋友都说了类似的话:他的同事/组长/领导...让他把状态全部放在Redux/Mobx中...他们认为不对,并没有知道如何反驳它。今天我们就来说说Redux、Mobx等状态管理库与React、Vue等视图库的关系,希望能解决以上困惑。产品的核心竞争力如果你在电梯里遇到一个大领导,他问你:小x,你最近在做什么功能?在电梯到达楼层前的短短2分钟,你如何向大领导描述你正在开发的功能?我想你会介绍功能的大体逻辑,不会介绍功能中某个按钮的具体交互逻辑吧?您将谈论逻辑,而不是互动。因为逻辑是最重要的。接下来,我们通过一个小故事来理解逻辑与交互的关系。逻辑与交互的关系有一天,老板让你开发文件上传功能。开发过程其实就是在处理与文件上传领域相关的各种状态之间的关系(比如上传进度,是否有错误...)。这部分状态称为域状态。逻辑开发完成后,您可以根据各种域状态编写单元测试。为了快速上线并验证是否使用了该功能,您将其直接发布为CLI工具。几天后,经过资料核实,发现该功能很受欢迎。所以你选择React作为视图库,按照前面的逻辑开发视图交互。在开发视图交互的过程中,需要处理与视图相关的各种状态(如加载、显示和隐藏、打开和关闭状态……)。这部分状态,我们称之为视图状态。可以看出,一个功能完备的产品包括域状态和视图状态。前者是必需的,后者是可选的。视图库中的状态下面说说React、Vue等视图库。由于大多数视图库都以组件作为模块边界,所以域状态和视图状态被分离到不同的组件中是很自然的,但是它们分离的方式是完全不同的。例如,一个完整的应用程序可以分为很多组件:从视图状态的角度来看这些组件:比较上下图,组件1(黄色和绿色)大小相同,这意味着这是一个纯组件具有自洽的交互逻辑(比如开关),他的交互逻辑不依赖于其他组件。除了组件1之外,更多的组件需要在尺寸上与其他组件互补,这意味着它们的交互逻辑会相互影响。比如组件5的长宽受组件2、6、8的影响,这可能意味着:组件5是一个提示框,是否弹出受2、6、8的影响。我们从领域状态(蓝色部分)的角度来看这些组件:整个应用的逻辑分散在不同的组件中。可能部分逻辑在组件1的didMount回调中,部分逻辑在组件3的useEffect回调中。由于组件5是提示框,只有提示效果,所以不包含逻辑(即域状态)应用程序运行所必需的。什么时候使用状态管理回到开头,什么样的状态应该放在状态管理中?对于视图状态:具有自洽状态的组件管理自己的状态(如组件1)状态相互影响的组件(如5和2、6、8)根据应用程序的复杂性和组件之间的跨度来确定。如果组件跨度比较近(如果是兄弟关系),可以将公共状态提升为公共父组件。如果组件跨度远,应用不复杂,可以提升为通用Context。如果应用程序很复杂,则考虑状态管理解决方案。对于域状态,因为它是以碎片的形式分布在不同的组件中,所以简单的小应用可以分布在组件中,或者提升到一个公共的Context中。在其他情况下,建议使用状态管理解决方案。甚至对于域状态的子域也可以在状态管理方案的基础上进行抽象和单独处理。比如服务端请求的数据的域状态更类似于缓存,可以在React中通过SWR或者ReactQuery进行处理。为了总结这篇文章,我们谈到了状态的分类——域状态和视图状态。这两种状态根据其特点有不同的解决方案。虽然把所有的状态都交给Redux去处理也不是不可以,但是势必会影响项目的可读性、性能和扩展性。做完这篇文章最好能说服同事/组长/领导。如果对方非要找Redux搭便车,在对付这种执着(笨)(笨)的人时,谨记四字格言:
