在React中使用Elm联合类型思路管理页面状态是否为最优方案?
问题解答
1. 这套Elm的状态管理思路在React中是否是最优方案?
对于你当前的场景(页面存在多个互斥的有限状态,且部分状态需要携带附加参数),这套基于*标签联合类型(Tagged Union)*的状态设计思路是非常优秀的方案,甚至可以说是该场景下的最优解之一,核心优势有两个:
- 完全避免非法状态:不用维护
isModalOpen、modalType、action三个分散的状态变量,不会出现「弹框开启状态下类型为空」这类矛盾的非法状态,能减少大量潜在bug - 逻辑可维护性强:所有可能的状态都集中定义,后续维护时一眼就能知道页面有多少种状态、每种状态需要什么参数,不会出现散落在各处的状态判断逻辑
唯一的适用限制是只适合互斥的有限状态场景,如果你的页面有很多并行的独立状态,完全可以和其他状态逻辑搭配使用,不用硬套这套模式。
2. 正确的实现方式
你当前的现有实现存在一个核心问题:把状态定义和渲染逻辑耦合在了一起,不符合Elm「状态是纯数据、渲染是状态的纯函数」的设计初衷,而且返回组件的状态没法存入React的useState、也没法在开发者工具中查看调试。正确的实现应该先定义纯数据结构的状态,再单独写渲染逻辑根据状态返回对应组件,参考代码如下:
原生JS实现(和你现有代码风格对齐)
首先定义全局的枚举和状态构造函数:
const ACTION = Object.freeze({ CHOOSE: "CHOOSE", EDIT: "EDIT", CONFIRM: "CONFIRM", }); const MODAL_TYPE = Object.freeze({ ADDRESS: "ADDRESS", PAYMENT: "PAYMENT", TAX: "TAX", }); // 状态构造函数,返回纯数据,和渲染逻辑完全解耦 const STATE = Object.freeze({ Normal: () => ({ tag: "Normal" }), PopupModal: (modalType, action) => ({ tag: "PopupModal", modalType, action }), Error: (msg) => ({ tag: "Error", msg }) });
在React组件中使用的示例:
import { useState } from 'react' import Normal from './Normal' import Address from './Address' import Payment from './Payment' import Tax from './Tax' function Page() { // 初始化页面状态为正常状态 const [pageState, setPageState] = useState(STATE.Normal()) // 单独抽离渲染逻辑,纯函数:输入状态返回组件 const renderPage = () => { switch(pageState.tag) { case "Normal": return <Normal onOpenModal={(type, action) => setPageState(STATE.PopupModal(type, action))} /> case "PopupModal": const { modalType, action } = pageState switch(modalType) { case MODAL_TYPE.ADDRESS: return <Address action={action} onClose={() => setPageState(STATE.Normal())} /> case MODAL_TYPE.PAYMENT: return <Payment action={action} onClose={() => setPageState(STATE.Normal())} /> case MODAL_TYPE.TAX: return <Tax action={action} onClose={() => setPageState(STATE.Normal())} /> default: return null } case "Error": return <div className="error">{pageState.msg}</div> default: return null } } return <div className="page-container">{renderPage()}</div> }
如果后续你使用TypeScript,还可以基于这套模式做穷尽性检查,避免漏写状态分支,体验和Elm的类型校验几乎一致。
内容的提问来源于stack exchange,提问作者Earn Asdsa
相关产品推荐
相关产品推荐

