帮助
支持我们

状态

现在我们知道了如何创建 HTML 元素和组件,以及如何使用 JSX 将道具和事件处理程序传递给两者,是时候学习如何更新虚拟 DOM 树了。

正如我们在上一章中提到的,函数组件和类组件都可以有状态 - 由组件存储的数据,用于更改其虚拟 DOM 树。当组件更新其状态时,Preact 使用更新后的状态值重新渲染该组件。对于函数组件,这意味着 Preact 将重新调用函数,而对于类组件,它将仅重新调用类的 render() 方法。我们来看一个每个示例。

类组件中的状态

类组件具有一个 state 属性,它是一个对象,其中包含组件在调用其 render() 方法时可以使用的数据。组件可以调用 this.setState() 来更新其 state 属性,并请求 Preact 重新渲染它。

class MyButton extends Component {
  state = { clicked: false }

  handleClick = () => {
    this.setState({ clicked: true })
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.clicked ? 'Clicked' : 'No clicks yet'}
      </button>
    )
  }
}

单击按钮将调用 this.setState(),这将导致 Preact 再次调用类的 render() 方法。现在 this.state.clickedtruerender() 方法返回一个 Virtual DOM 树,其中包含文本“已单击”而不是“尚未单击”,从而导致 Preact 在 DOM 中更新按钮的文本。

使用 hook 在函数组件中设置状态

函数组件也可以有状态!虽然它们没有像类组件那样的 this.state 属性,但 Preact 附带了一个微小的附加模块,它提供了一些函数,用于存储和处理函数组件内部的状态,称为“hook”。

Hook 是可以在函数组件内调用的特殊函数。它们很特别,因为它们会记住跨渲染的信息,有点像类上的属性和方法。例如,useState hook 返回一个包含一个值和一个“setter”函数的数组,可以调用该函数来更新该值。当一个组件被调用(重新渲染)多次时,它所做的任何 useState() 调用每次都会返回完全相同的数组。

ℹ️ hook 实际上是如何工作的?

在幕后,像 setState 这样的 hook 函数通过将数据存储在与 Virtual DOM 树中每个组件关联的一系列“槽”中来工作。调用一个 hook 函数会使用一个槽,并增加一个内部“槽号”计数器,以便下一个调用使用下一个槽。Preact 在调用每个组件之前重置此计数器,因此每次渲染组件时,每个 hook 调用都会与同一个槽相关联。

function User() {
  const [name, setName] = useState("Bob")    // slot 0
  const [age, setAge] = useState(42)         // slot 1
  const [online, setOnline] = useState(true) // slot 2
}

这称为调用站点排序,这就是为什么 hook 必须始终在组件内以相同的顺序调用,并且不能有条件地调用或在循环内调用。

让我们看一个 useState hook 的实际示例

import { useState } from 'preact/hooks'

const MyButton = () => {
  const [clicked, setClicked] = useState(false)

  const handleClick = () => {
    setClicked(true)
  }

  return (
    <button onClick={handleClick}>
      {clicked ? 'Clicked' : 'No clicks yet'}
    </button>
  )
}

单击按钮会调用 setClicked(true),它会更新由 useState() 调用创建的状态字段,进而导致 Preact 重新渲染此组件。当组件第二次被渲染(调用)时,clicked 状态字段的值将为 true,返回的虚拟 DOM 将包含文本“Clicked”,而不是“No clicks yet”。这将导致 Preact 更新 DOM 中按钮的文本。


试一试!

让我们尝试创建一个计数器,从我们在上一章中编写的代码开始。我们需要在状态中存储一个 count 数字,并在单击按钮时将其值增加 1

由于我们在上一章中使用了函数组件,因此使用钩子可能最简单,不过你可以选择任何你喜欢的存储状态的方法。

加载中...