React教程: 第6天 状态

本文转载自:众成翻译
译者:iOSDevLog
链接:https://www.zcfy.cc/article/3824
原文:https://www.fullstackreact.com/30-days-of-react/day-6/

原著PDF版本下载:下载超过300页的 PDF

今天我们开始了解React中有状态组件的工作原理,并且看看我们何时以及为什么要使用状态。

我们几乎完成了在React开始运行的第一周。我们通过JSX工作,构建我们的第一个组件,设置父子关系,并使用React驱动组件属性。我们还有一个重要的想法,我们还没有讨论React _状态_相关的知识。

有关状态的事

React并没有让我们修改this.props 我们有充分的理由的组件。想象一下,如果我们将 title 属性支持传递给Header 组件,并且Header 组件能够修改它。我们如何知道titleHeader 组件的什么 ?我们设置了竞争条件,混乱的数据状态,并且将是一个全面的坏主意,修改由父组件传递给我们的变量并在小孩中修改。

然而,有时组件需要能够更新自己的状态。例如,active 在秒表上设置标志或更新计时器。

虽然最好props 尽可能多地使用,但有时我们需要坚持组件的状态。为了处理这个问题,React使我们有能力在组件中拥有状态

组件里的state 意图完全是内部的组件,它的孩子(即组件和任何孩子使用它访问)。类似于我们如何props 在组件中访问,可以通过this.state 组件访问状态。无论何时状态改变(通过 this.setState() ),组件将重新投递。

例如,假设我们有一个简单的时钟组件来显示当前时间:
(原文中的时钟是一直在与系统时间同步的,我这里就没法显示了,只能放一个截图了)
在这里插入图片描述

即使这是一个简单的时钟组件,它确实保留状态,因为它需要知道当前显示的时间。没有使用·state·,我们可以设置一个计时器并重新渲染整个React组件,但页面上的其他组件可能不需要重新渲染…这将是一个头痛的问题。

相反,我们可以设置一个计时器来调用组件内部的rerender并更改此组件的内部状态。

我们来建立这个组件。首先,我们将创建我们将要调用的组件Clock

在进入状态之前,我们来构建组件并创建该render() 函数。我们需要考虑数字,如果数字小于10,在数字前面加上一个零(0 ),并进行相应的设置 am/pmrender()函数的最终结果可能如下所示:

class Clock extends React.Component {
  render() {
    const currentTime = new Date(),
          hours = currentTime.getHours(),
          minutes = currentTime.getMinutes(),
          seconds = currentTime.getSeconds(),
          ampm = hours >= 12 ? 'pm' : 'am';

    return (
      <div className="clock">
        {
          hours == 0 ? 12 :
            (hours > 12) ?
              hours - 12 : hours
        }:{
          minutes > 9 ? minutes : `0${minutes}`
        }:{
          seconds > 9 ? seconds : `0${seconds}`
        } {ampm}
      </div>
    )
  }
}
// ...
export default Clock

替代填充技术
或者,我们可以使用简短的代码片段来填充时钟时间:

("00" + minutes).slice(-2)

但我们选择了更清楚地理解前面的代码。

如果我们渲染我们的新Clock 组件,我们只会在组件本身重新运行时获得时间。这不是一个非常有用的时钟(还)。为了将静态时间显示Clock 组件转换为显示时间的时钟,我们需要每秒更新一次。

为了做到这一点,我们需要跟踪组件状态下的current 时间。 为此,我们需要设置初始状态值。

为此,我们首先创建一个getTime()函数,该函数返回包含 hours, minutes, seconds and ampm值的javascript对象。我们将调用此函数来设置我们的状态。

class Clock extends React.Component {
  //...
  getTime() {
    const currentTime = new Date();
    return {
      hours: currentTime.getHours(),
      minutes: currentTime.getMinutes(),
      seconds: currentTime.getSeconds(),
      ampm: currentTime.getHours() >= 12 ? 'pm' : 'am'
    }
  }
  // ...
}

在ES6类样式中,我们可以在constructor() 中通过设置 this.state 来为组件设置一个初始状态(getTime()函数的返回值).。

 constructor(props) {
    super(props);
    this.state = this.getTime();
  }

this.state 现在看起来像下面的对象

{
  hours: 11,
  minutes: 8,
  seconds: 11,
  ampm: "am"
}

构造函数的第一行应该始终调用 super(props)。如果您忘记了这一点,组件将不会非常喜欢(即会有错误)。

现在我们 this.stateClock 组件中有一个定义,我们可以在 render() 函数中引用它 this.state。让我们更新我们的 render() 函数 this.state 来获取以下值:

class Clock extends React.Component {
  // ...
  render() {
    const {hours, minutes, seconds, ampm} = this.state;
    return (
      <div className="clock">
        {
          hours === 0 ? 12 :
            (hours > 12) ?
              hours - 12 : hours
        }:{
          minutes > 9 ? minutes : `0${minutes}`
        }:{
          seconds > 9 ? seconds : `0${seconds}`
        } {ampm}
      </div>
    )
  }
}

现在,我们可以更新组件的状态并将 render() 函数与数据管理分离,而不是直接使用数据值。

为了更新状态,我们将使用该函数 this.setState(),这将触发组件重新渲染。

我们需要对组件的这个值调用 setState(),因为它是反应组分类的子类。

在我们的Clock 组件中,我们使用本机 setTimeout() JavaScript函数创建一个定时器,以 this.state 在1000毫秒内更新对象。我们将把这个功能放在一个函数中,我们再次调用它。

class Clock extends React.Component {
  // ...
  constructor(props) {
    super(props);
    this.state = this.getTime();
  }
  // ...
  setTimer() {
    clearTimeout(this.timeout);
    this.timeout = setTimeout(this.updateClock.bind(this), 1000);
  }
  // ...
  updateClock() {
    this.setState(this.getTime, this.setTimer);
  }
  // ...
}

要在呈现我们的组件之后立即开始更新计时器,我们在React的生命周期方法 componentDidMount 中调用 this.setTimer 设置计时器。我们将在下一节中介绍生命周期中的钩子。

updateClock() 函数中,我们将要在新时间内更新状态。我们现在可以在 updateClock() 函数中更新状态:

class Clock extends React.Component {
  // ...
  updateClock() {
    this.setState(this.getTime, this.setTimer);
  }
  // ...
}

组件将被安装在页面上,并每秒更新一次(大约每1000毫秒一次)。

现在,组件本身可能会比超时功能再次调用慢,这将导致重新出现的瓶颈,并且不必要地在移动设备上使用宝贵的电池。在调用 setTimer() 函数之后 this.setState(),我们可以将第二个参数传递给 this.setState() 函数,该函数将在状态更新后保证被调用。

class Clock extends React.Component {
  // ...
  updateClock() {
    const currentTime = new Date();
    this.setState({
      currentTime: currentTime
    }, this.setTimer);
  }
  // ...
}

这是我们的完整 Clock 组件代码。

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = this.getTime();
  }
  
  componentDidMount() {
    this.setTimer();
  }

  setTimer() {
    clearTimeout(this.timeout);
    this.timeout = setTimeout(this.updateClock.bind(this), 1000);
  }

  updateClock() {
    this.setState(this.getTime, this.setTimer);
  }

  getTime() {
    const currentTime = new Date();
    return {
      hours: currentTime.getHours(),
      minutes: currentTime.getMinutes(),
      seconds: currentTime.getSeconds(),
      ampm: currentTime.getHours() >= 12 ? 'pm' : 'am'
    }
  }

  render() {
    const {hours, minutes, seconds, ampm} = this.state;
    return (
      <div className="clock">
        {hours == 0 ? 12 : hours > 12 ? hours - 12 : hours}:
        {minutes > 9 ? minutes : `0${minutes}`}:
        {seconds > 9 ? seconds : `0${seconds}`} {ampm}
      </div>
    );
  }
}

样式


由于我们在本课程中没有重点关注CSS,因此我们没有涵盖特定于构建时钟的CSS,正如您在屏幕上看到的那样。
不过,我们想确保你造的钟和我们的钟相似。如果您在代码中包含以下CSS作为 <link/> 标记,那么您的时钟将看起来相似,并且将使用我们使用的相同样式:

<link href="https://cdn.jsdelivr.net/gh/fullstackreact/30-days-of-react@master/day-06/public/Clock.css" rel="stylesheet" type="text/css" />

有些事情要记住
当我们调用 this.setState() 一个对象参数时,它将执行一个数据的浅合并到可用的对象中 this.setState(),然后重新渲染组件。

我们通常只想在我们的状态中保持我们将在该 render() 函数中使用的值。从上面我们的时钟的例子,请注意,我们的存储 hours, minutes ,以及 seconds 在我们的状态。在我们不打算在 render 功能中使用的状态下存储对象或计算通常是一个坏主意,因为它可能导致不必要的渲染和浪费的CPU周期。

正如我们在本节顶部指出的那样,props 不仅出于性能原因,最好使用,但是因为有状态的组件更难测试。

今天,我们更新了我们的组件以使其处于状态状态,现在有必要处理如何使组件成为状态。明天我们将进入组件的生命周期,何时/如何与页面进行交互。


上一章:数据驱动
下一章:生命周期钩子函数


本教程系列的完整源代码可以在 GitHub repo, 上找到,其中包含所有样式和代码示例。
如果在任何时候你感到困扰,有进一步的问题,请随时通过以下方式与我们联系:
在原文文章末尾评论这篇文章
发送电子邮件至 [email protected]
加入我们的 gitter room
发推文给我们 @fullstackreact
REACT.JS DOM 应用 移动 JAVASCRIPT
版权声明 本译文仅用于学习、研究和交流目的,欢迎非商业转载。转载请注明出处、译者和众成翻译的完整链接。
原文链接:全栈React: React 30天


如果有疑问可以直接留言评论,如果觉得对你有帮助,可以小小的赞赏一杯奶茶钱,谢谢!!

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值