React 组件状态 (State)
在前面的章节中,我们学习了 props,它允许我们将数据从父组件传递到子组件。然而,props 是只读的。组件不能改变自己的 props。
那么,当一个组件需要根据用户交互、网络响应或其他事件来改变自己的数据时,该怎么办呢?答案就是 State (状态)。
State 与 props 类似,但它是私有的,并且完全由组件自己控制。
引入 useState Hook
在函数组件中,我们使用 useState Hook 来为组件添加 state。
什么是 Hook? Hook 是 React 16.8 版本引入的新特性。它们是一些特殊的函数,可以让你在函数组件中使用 state 以及其他 React 特性。我们将在后面的章节中深入学习 Hooks。
让我们来看一个例子。我们将创建一个有时钟功能的组件,它会自己更新时间。
之前我们用 setInterval 和 root.render() 来实现,但那不是组件化的思想。现在,我们让 Clock 组件自己管理自己的状态。
代码解析
-
const [date, setDate] = useState(new Date());- 我们调用
useStateHook,并传入初始状态new Date()。 useState返回一个包含两个值的数组:- 当前的状态值 (
date)。 - 一个可以用来更新状态的函数 (
setDate)。
- 当前的状态值 (
- 我们使用数组解构语法
[date, setDate]来给这两个值命名。
- 我们调用
-
useEffect(...)useEffectHook 用于处理副作用(side effects),比如数据获取、订阅或者手动修改 DOM。- 在这里,我们用它来设置一个每秒钟都会执行的定时器
setInterval。 useEffect的第二个参数[]是一个依赖数组。当数组为空时,意味着这个 effect 只会在组件第一次渲染到屏幕上(挂载)后执行一次。useEffect返回的函数是一个清理函数。它会在组件从屏幕上移除(卸载)时执行,用于清除定时器,防止内存泄漏。
-
setDate(new Date())- 在定时器的回调函数中,我们调用
setDate并传入一个新的Date对象。 - 这是更新 state 的唯一正确方式。
- 当
setDate被调用时,React 会重新渲染Clock组件,并使用最新的date值。
- 在定时器的回调函数中,我们调用
State 更新是异步的
当你调用 setState 函数时,React 可能会将多个 setState 调用合并成一个单一的更新来提高性能。这意味着 this.props 和 this.state 可能是异步更新的,你不应该依赖它们的值来计算下一个状态。
例如,下面的代码可能不会按预期工作:
因为 count 的值在同一次渲染中不会改变,所以三次调用 setCount 的效果和一次是相同的。
正确的做法:使用函数式更新
如果你需要基于前一个 state 来计算新 state,你可以向 setState 函数传递一个函数。这个函数会接收前一个状态作为参数,并返回新的状态。
现在,每次调用 setCount 都会接收到上一次更新后的最新 state,最终 count 会增加 3。
总结
state用于存储组件内部会随时间变化的数据。- 使用
useStateHook 在函数组件中添加和管理 state。 - 调用
useState返回当前 state 和一个更新 state 的函数[value, setValue]。 - 通过调用
setValue函数来更新 state,这会触发组件的重新渲染。 - State 更新可能是异步的,当新 state 依赖于旧 state 时,应使用函数式更新。
接下来,我们将学习组件的另一个重要概念:生命周期。