React 状态的故事。

2025-06-08

React 状态的故事。

维护状态很难,维护 UI 更难,而且几乎不可能让用户满意。

无论你计划得多么周全,崩溃总是会发生。

React 是一个著名的 Web UI 库,其状态实现历史悠久。组件塑造了应用程序。但状态的作用是确保应用程序处于合理的范围内。

除非它在实现这个目标之前就让开发人员发疯!


不同的季节,不同的状态。

我们知道四季是如何更替的。春天百花齐放。夏天汗水顺着腋窝流淌。秋天落叶纷纷。谁会忘记冬天的雪景呢?(提示:生活在全球变暖之后的人们首次登场!)

大自然就像一个巨大的React Component……是地球生命的一部分。但它会经历不同的阶段。我们把每个阶段称为一个季节。

前端应用程序的不同部分也可能处于不同的阶段。它们是由我们所说的变化引起的state


我们的研究介绍。

想象一下一个脾气暴躁的人。他在炎热的夏天骂脏话。天气变冷的时候又会打人。没人能预料到他会怎样。

你可能会说他受到天气的影响。

比如,春天里他很善良,下雨天也会吟诗。冬天寒冷,让他不耐烦。所以他更愿意用行动而不是空谈来达成交易。

这样的人缺乏良好的状态管理,无法控制自己的行为。类似的问题也可能存在于前端应用程序中。这时,它会给出不恰当的响应,甚至根本不给出响应。

然后,我们来快速回顾一下 React 组件中的状态是什么。


React 组件状态。

来自React 文档

State 与 props 类似,但它是私有的并且完全由组件控制。

Props它们本身就是Objects。它们是我们通过 传递给组件keys的名称。它们是相应属性的名称。attributesJSXvalues

状态可以是属性,也可以是属性的集合,它可以是ObjectString或任何其他JavaScript Type。但是状态和 之间的主要区别是什么prop

答案:从其父级component接收,同时它创建并拥有propscontrolsstate


实际问题是什么?

此时我们可以问一个问题:什么构成了一个伟大的应用程序?

或许是坚持到底的奉献精神。渴望带来满足感。一种有用的感觉。我们或许可以称之为自信。

糟糕的应用程序不会受到这些东西的推动。它已经感到满足和自信了。它受更坚实的东西引导。它受数据流的影响。受强大的惯性的影响。

它对用户期望漠不关心。而且它很少适用于不同的用户故事。它只有在一切正常的情况下才能正常工作。但在实际应用中,这种情况很少见。


我们可以使用一个简单的变量来创建一个状态吗?

假设我们有一个名为 的组件Nature。该组件有一个主要状态变量:season。首先,让我们使用一个简单的 来声明它JavaScript variable

function Nature(props) {
  let season = 'spring';

  return(
    <p>Currently it is {season}</p>
  )
}
Enter fullscreen mode Exit fullscreen mode

这一行const season = 'spring';是我们声明状态变量的地方。输出的 HTML 如下所示:

<p>Currently it is spring</p>
Enter fullscreen mode Exit fullscreen mode

让我们在函数的返回中添加一个按钮。我们给它的onClick事件传递一个回调,它会尝试将变量season的值更改为“summer”:

function Nature(props) {
  let season = 'spring';

  const changeSeason = () => {
    season = 'summer'
  }

  return(
    <div>
      <p>Currently it is {season}</p>
      <button onClick={changeSeason}>Click to change season!</button>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

它会输出之前的结果,并添加一个按钮。但是,如果你点击该按钮,它不会改变输出中的季节名称。

变量的值变为夏天,但onClick不会导致输出发生变化。

为什么?

要回答这个问题,我们需要了解React Hooks Flow。事情从这里开始变得有趣。


组件疯狂的故事。

事件。一系列事件构成了我们所熟知的故事情节。例如,你可能刚刚高中毕业。这对你来说是一个重要的事件,也体现了你的教育水平。

我们的记忆和个性是由事件构成的。我们的简历也完全由它们填满。为了让生活继续前进,我们也期待着未来的事件。

组件的生命周期充满了事件。我们来看一个例子。

假设有一个提交按钮用于注册用户信息。该按钮处于禁用状态,直到用户正确填写必填字段。表单组件使用状态变量来启用该按钮。

一切运行正常。用户填写表单。灰色按钮变为蓝色,用户点击。后台会发送注册 API 请求。但用户并未收到任何通知。他们没有看到任何加载元素或消息。

他们以为没起作用,又点击了一次。结果呢?后台又发了个请求。第一个请求成功注册了用户。
组件的设计初衷是成功后重定向用户。但是……

第二个请求的响应很快就来了。组件确认用户的电子邮件重复。现在,另一个保存电子邮件重复错误的状态变量变为 true。

该组件向用户显示错误信息,告知用户该邮件重复,并且由于错误状态不为 false,重定向无法进行。组件已被告知,如果错误状态不明确,则不要重定向。这无关紧要,而且是一种糟糕的状态管理。

在这个例子中,我们面临几个事件。其中一个事件发生在表单填写时。另一个发生在用户点击按钮时。最后一个事件发生在响应到达时。

我们作为用户和开发者可以理解这些事件。但是像 React 这样的 UI 库并不像人类那么智能。它必须事先被告知这些事件是什么,并且必须事先注册它们。

这就是我们所说的Components Hook Flow


React 组件钩子流

从 React 16.8 版本开始,Hooks 被添加到 React 中。这在当时是一个重大更新,因为它赋予了无状态函数组件只有在类组件中才有的功能。

在函数式组件中能够运行效果并更改状态是一件好事。但是类组件也为开发人员提供了生命周期方法,例如 componentDidMount 或 shouldComponentUpdate。

函数式组件和钩子不提供生命周期方法。相反,函数式组件中的不同钩子会按照特定的顺序运行,以便开发人员可以根据此顺序实现相同的生命周期逻辑。

Hooks Flow 由 3 个主要阶段组成:MountUpdateUnmount

组件首次挂载时,会设置其初始值,包括useStateuseReducer初始化函数。然后,组件将继续执行您在函数式组件中添加的其余代码,直到达到返回值。
在渲染返回的 JSX 之前,它会运行您layoutEffects使用钩子创建的useLayoutEffect。之后,浏览器会绘制屏幕以反映 React 虚拟 DOM。然后,effects您注册的useEffect会被调用。

组件的挂载流程很简单。但之后组件需要更新。这可能由两个原因导致:要么 props 发生变化,要么 state 更新。

更新阶段有其自己的步骤:它将运行你的函数式组件,并根据更新状态导致的新变化更新 DOM。
下一步,它将清除之前的布局效果 (layoutEffects),之后,它将运行布局效果 (layoutEffects)。浏览器会重新绘制屏幕以反映更改。
最后,在运行效果之前,React 将清除之前的效果。

更新阶段的步骤顺序与挂载阶段类似,仅在细节上有所不同。例如,挂载阶段使用初始状态值,而更新阶段使用新的状态值。挂载阶段会运行效果,但更新阶段会先尝试清除组件先前更新或挂载阶段造成的效果。

正如我们提到的,此流程的第三阶段是卸载阶段。在此阶段,整个组件将从屏幕上清除。因此,除了 React 尝试清除所有剩余的布局效果和效果之外,什么也不会发生。

现在我们知道了 React Hooks Flow,我们就可以明白为什么将组件的状态存储在一个简单的变量中不会导致 DOM 发生变化。

因为 React 不会更新 DOM,除非它确定某些东西发生了变化。

React 监听状态变化的方式类似于我们在 JS 中添加事件监听器。例如,假设有一个文本输入元素。我们可以为其value change,或者当输入 时添加监听器blurred

React 为状态变量的变化添加了监听器。当我们调用 stateSetter 函数时,就会触发此事件,然后 React 就知道如何更新 DOM。

然后,让我们重写之前的代码以使其工作。


宣告国家的正确方式

React 提供了一个useState钩子,用于将状态引入函数式组件。要初始化状态,你需要调用该钩子并将状态的 传递给它initial value
该钩子将返回一个包含两个元素的数组。数组中的第一个元素是state's value,第二个元素是state-setter function

我们将使用此函数将季节状态添加到组件中。我们还将重写该changeSeason函数以使用state-setter返回的函数useState

function Nature(props) {
  let [season, setSeason] = useState('spring');

  const changeSeason = () => {
    setSeason('summer')
  }

  return(
    <div>
      <p>Currently it is {season}</p>
      <button onClick={changeSeason}>Click to change season!</button>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

现在,渲染此组件后,如果我们尝试单击按钮,我们将看到段落中的季节名称更改为summer

在上面的代码中,变量season指的是 useState 返回的第一个元素,该元素保存着最新的状态值。setSeason 是我们可以用来更新状态值的方法。

我们学习了如何将状态添加到 React 的函数式组件中。我们还学习了状态的更基本工作原理。

了解了声明状态的正确方法后,一个很好的问题出现了:在 React 中是否也有使用状态的正确方法?


生活是一场旅程(React 开发也是如此)

没有人是完全一样的。没有人的兴趣和其他人完全相同。我们每个人的喜好都独一无二。而这种独特性也影响着我们的生活方式。

不同的 React 项目也有各自的差异。它们在管理状态、效果、组件树甚至文件夹结构方面都有所不同。

没有人规定你必须以特定的方式构建 React 项目。你必须了解 React 用于管理其重新渲染、props、状态、效果等的底层思维方式。

通过本文,我们了解了 React 实现状态背后的理念。我希望它能帮助你更好地理解状态是什么以及它为什么重要。

这篇文章已经很长了,我不想再用多余的信息烦你,就此结束吧。不过,我再补充一点建议。每当你犹豫是否应该使用某个状态时,试着回答这个问题:这个变量的改变是否应该导致重新渲染?

封面图片来源:M. Schuppich/Shutterstock。


我还没有在英文推特上创作内容的经验。但我想开始在我的推特账户上发布关于不同编程主题的推文。如果您能关注我的推特账户,我将不胜感激。:)

鏂囩珷鏉ユ簮锛�https://dev.to/alimobasheri/a-story-of-react-states-5di4
PREV
使用 Go 和 TDD 构建财务跟踪 REST API - 第 1 部分
NEXT
使用 Flask-RESTful 构建基本 RESTful API