状态管理:如何区分坏布尔值和好布尔值观察

2025-05-25

状态管理:如何区分坏布尔值和好布尔值

观察

TL;DR:坏的布尔值代表状态。好的布尔值源自状态。

在应用中管理状态时,很容易被错误的布尔值所困扰。错误的布尔值如下所示:

let isLoading = true;
let isComplete = false;
let hasErrored = false;
Enter fullscreen mode Exit fullscreen mode

表面上看,这段代码不错。看起来你用合适的布尔值名称表示了三个独立的状态。在你为状态绘制的“模型”中,任何时刻只有一个状态可以为真。

在获取请求中,您可以像这样建模状态:

const makeFetch = async () => {
  isLoading = true;
  try {
    await fetch('/users');

    isComplete = true;
  } catch (e) {
    hasErrored = true;
  }
  isLoading = false;
};
Enter fullscreen mode Exit fullscreen mode

再次,这看起来很棒。我们在处理异步请求时协调了布尔值。

但是这里有个 bug。如果我们获取数据成功了,然后我们再次获取数据,会发生什么?最终会得到:

let isLoading = true;
let isComplete = true;
let hasErrored = false;
Enter fullscreen mode Exit fullscreen mode

隐式状态

你在创建初始模型时可能没有考虑到这一点。你可能有一些前端组件正在检查isComplete === trueisLoading === true。最终你可能会得到一个加载旋转器同时显示之前的数据。

这怎么可能呢?嗯,你已经创建了一些隐式状态。假设你考虑了三种你真正想要处理的状态:

  1. loading:加载数据
  2. complete:显示数据
  3. errored:如果数据没有出现,则出错

嗯,你实际上允许了8 个状态!第一个布尔值是 2,第二个布尔值乘以 2,第三个布尔值也乘以 2。

这就是所谓的布尔爆炸——我从Kyle Shevlin 的 egghead 课程中了解到了这一点。

使状态明确

如何解决这个问题?我们需要一个包含 3 个可能值的系统,而不是 8 个。在 Typescript 中,我们可以使用枚举来实现这一点。

type Status = 'loading' | 'complete' | 'errored';

let status: Status = 'loading';
Enter fullscreen mode Exit fullscreen mode

我们将通过如下方式实现这一点:

const makeFetch = async () => {
  status = 'loading';
  try {
    await fetch('/users');

    status = 'complete';
  } catch (e) {
    status = 'errored';
  }
};
Enter fullscreen mode Exit fullscreen mode

现在不可能同时处于“加载中”和“完成”状态了——我们已经修复了这个问题。我们把错误的布尔值变成了正确的枚举值。

制作好的布尔值

但并非所有布尔值都是不好的。许多流行的库,例如react-queryapollo和 ,都urql在其状态中使用布尔值。以下是示例实现:

const [result] = useQuery();

if (result.isLoading) {
  return <div>Loading...</div>;
}
Enter fullscreen mode Exit fullscreen mode

这些布尔值之所以是好的布尔值,是因为它们的底层机制基于枚举。不好的布尔值代表状态。好的布尔值源自状态:

let status: Status = 'loading';

// Derived from the status above
let isLoading = status === 'loading';
Enter fullscreen mode Exit fullscreen mode

您可以安全地使用它isLoading来显示您的加载微调器,并很高兴地知道您已经删除了所有不可能的状态。

附录:JavaScript 中的枚举

评论里有几个人问如何在 JavaScript 中表示状态枚举。虽然上面的代码不需要类型定义也能运行,但你也可以将枚举表示为对象类型。

const statusEnum = {
  loading: 'loading',
  complete: 'complete',
  errored: 'errored',
};

let status = statusEnum.loading;

const makeFetch = async () => {
  status = statusEnum.loading;
  try {
    await fetch('/users');

    status = statusEnum.complete;
  } catch (e) {
    status = statusEnum.errored;
  }
};
Enter fullscreen mode Exit fullscreen mode
文章来源:https://dev.to/mattpocockuk/state-management-how-to-tell-a-bad-boolean-from-a-good-boolean-260n
PREV
微服务路线图
NEXT
编写自文档代码