夜猫子的知识栈 夜猫子的知识栈
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《Web Api》
    • 《ES6教程》
    • 《Vue》
    • 《React》
    • 《TypeScript》
    • 《Git》
    • 《Uniapp》
    • 小程序笔记
    • 《Electron》
    • JS设计模式总结
  • 《前端架构》

    • 《微前端》
    • 《权限控制》
    • monorepo
  • 全栈项目

    • 任务管理日历
    • 无代码平台
    • 图书管理系统
  • HTML
  • CSS
  • Nodejs
  • Midway
  • Nest
  • MySql
  • 其他
  • 技术文档
  • GitHub技巧
  • 博客搭建
  • Ajax
  • Vite
  • Vitest
  • Nuxt
  • UI库文章
  • Docker
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

夜猫子

前端练习生
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《Web Api》
    • 《ES6教程》
    • 《Vue》
    • 《React》
    • 《TypeScript》
    • 《Git》
    • 《Uniapp》
    • 小程序笔记
    • 《Electron》
    • JS设计模式总结
  • 《前端架构》

    • 《微前端》
    • 《权限控制》
    • monorepo
  • 全栈项目

    • 任务管理日历
    • 无代码平台
    • 图书管理系统
  • HTML
  • CSS
  • Nodejs
  • Midway
  • Nest
  • MySql
  • 其他
  • 技术文档
  • GitHub技巧
  • 博客搭建
  • Ajax
  • Vite
  • Vitest
  • Nuxt
  • UI库文章
  • Docker
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 核心概念

    • JSX简介
    • 元素渲染
    • 组件&Props
    • State&生命周期
      • 生命周期
      • 将函数组件转换成 class 组件
      • 向 class 组件中添加局部的 state
      • 将生命周期方法添加到 Class 中
      • 正确地使用 State
        • 不要直接修改 State
        • State 的更新可能是异步的
        • State 的更新会被合并
      • 数据是向下流动的(单向数据流)
        • 每个组件都是真正独立的。
    • 事件处理
    • 条件渲染
    • 列表&Key
    • 表单
    • 状态提升 (共享状态)
    • 组合vs继承
    • React哲学
  • 高级指引

  • Hook

  • 案例演示

  • Router

  • Redux

  • Jotai

  • Mobx

  • 其他

  • 《React》笔记
  • 核心概念
夜猫子
2021-10-16
目录

State&生命周期

# 04. State & 生命周期

State 与 props 类似,但是 state 是私有的,并且完全受控于当前组件.

State类似于vue中的data选项

# 生命周期

React16 自上而下地对生命周期做了另一种维度的解读:

  • **Render阶段:**计算一些必要的状态信息。
  • **Pre-commit阶段:**这里指的是“更新真正的 DOM 节点”这个动作。该阶段并没有去更新真实的 DOM,不过 DOM 信息已经是可以读取(包括 ref)。
  • Commit 阶段:React 会完成真实 DOM 的更新工作。

与此同时,新的生命周期在流程方面,仍然遵循“挂载”、“更新”、“卸载”这三个广义的划分方式。它们分别对应到:

挂载过程:

  • constructor
  • getDerivedStateFromProps
  • render
  • componentDidMount

更新过程:

  • getDerivedStateFromProps
  • shouldComponentUpdate
  • render
  • getSnapshotBeforeUpdate
  • componentDidUpdate

卸载过程:

  • componentWillUnmount

# 将函数组件转换成 class 组件

在没有useState钩子函数之前,是通过class组件管理State ?

class Clock extends React.Component {
  render() {
    return (
    	<div>
      	<h1>Hello, world</h1>
        <h2>It is {this.props.date.toLocalTimeString()}</h2>
      </div>
    )
  }
}
1
2
3
4
5
6
7
8
9
10

每次组件更新时 render 方法都会被调用,但只要在相同的 DOM 节点中渲染 <Clock /> ,就仅有一个 Clock 组件的 class 实例被创建使用。这就使得我们可以使用如 state 或生命周期方法等很多其他特性。

单例模式?

# 向 class 组件中添加局部的 state

class Clock extends React.Component {
  // 第二步:添加构造函数,并给 this.state 赋初始值
  constructor(props) {
    super(props) // 通过super将props传递到父类构造函数中,并调用父类构造函数
    this.state = {date: new Date()}
  }
  render() {
    // 第一步:render 方法内,将 this.props 替换成 this.state
    return (
    	<div>
      	<h1>Hello, world</h1>
        <h2>It is {this.state.date.toLocalTimeString()}</h2>
      </div>
    )
  }
}


ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 将生命周期方法添加到 Class 中

在具有许多组件的应用程序中,当组件被销毁时释放所占用的资源是非常重要的。

当 Clock 组件第一次被渲染到 DOM 中的时候,就为其设置一个计时器 (opens new window)。这在 React 中被称为“挂载(mount)”。

同时,当 DOM 中 Clock 组件被删除的时候,应该清除计时器 (opens new window)。这在 React 中被称为“卸载(unmount)”。

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  // 挂载 - 挂载完成后执行
  componentDidMount() {
    // 组件挂载后开启定时器
    this.timerID = serInterval( // 可以向this添加任意属性字段
    	() => this.tick(), 1000
    )
  }

  // 卸载
  componentWillUnmount() {
    // 组件卸载后清除定时器,释放内存
    clearInterval(this.timerID)
  }

  tick() {
    this.setState({
      date: new Date()
    })
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

# 正确地使用 State

关于 setState() 你应该了解三件事:

# 不要直接修改 State


// Wrong 不要直接修改state
this.state.comment = 'Hello'

// Correct 应使用 setState() 方法
this.setState({comment: 'Hello'})
1
2
3
4
5
6

构造函数是唯一可以给 this.state 直接赋值的地方

setState是React内部方法,使state更新具有响应式?

这是框架设计之初刻意为之,react通过迫使你调用setState去更新视图是为了让你意识到你正在改变组件的状态。

这样可以让开发者在潜意识中认为改变state是一件严肃的事情,一定程度上规范开发行为。

以上是setState为什么存在,下面我们来看它的意义。

1.任务驱动的更新

react实现更新的原理并不是vue那样的双向绑定,(vue用的是gettet和setter(3.0用的Proxy),在你赋值时它就知道哪个字段变了,于是也就知道该更新哪个组件)而是在你设置state之后,随之产生一个更新任务,任务的出现触发更新调度。setState的作用正是去创建任务,标记哪个fiber节点产生了更新,并触发任务调度。如果直接修改,对象变了,但还是同一个对象,react并不知道数据已经变了。

2.任务优先级

setState创建更新时要从scheduler中拿到触发本次更新的优先级,将优先级加入到任务中。

setState作为更新的入口,它是任务下发的起点。也不是说直接改变state就完全不能实现上述两点,但直接改变state想必是要有依赖收集的过程,但react中并不存在依赖收集,直接改了就是改了,并没有触发任何更新相关的流程。

# State 的更新可能是异步的

出于性能考虑,React 可能会把多个 setState() 调用合并成一个调用。

因为 this.props 和 this.state 可能会异步更新,所以你不要依赖他们的值来更新下一个状态。

要解决这个问题,可以让 setState() 接收一个函数而不是一个对象。这个函数用上一个 state 作为第一个参数,将此次更新被应用时的 props 做为第二个参数:

// Correct
this.setState((state, props) => ({
  counter: state.counter + props.increment
}));
1
2
3
4

# State 的更新会被合并

当你调用 setState() 的时候,React 会把你提供的对象合并到当前的 state。

  constructor(props) {
    super(props);
    this.state = {
      posts: [],
      comments: []
    };
  }

// 分别调用setState单独更新state下的属性
 componentDidMount() {
    fetchPosts().then(response => {
      this.setState({
        posts: response.posts
      });
    });

    fetchComments().then(response => {
      this.setState({
        comments: response.comments
      });
    });
  }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

这里的合并是浅合并,所以 this.setState({comments}) 完整保留了 this.state.posts, 但是完全替换了 this.state.comments。

# 数据是向下流动的(单向数据流)

不管是父组件或是子组件都无法知道某个组件是有状态的还是无状态的,并且它们也并不关心它是函数组件还是 class 组件。

这就是为什么称 state 为局部的或是封装的的原因。除了拥有并设置了它的组件,其他组件都无法访问。

组件可以选择把它的 state 作为 props 向下传递到它的子组件中:

<FormattedDate date={this.state.date} />
1

FormattedDate 组件会在其 props 中接收参数 date,但是组件本身无法知道它是来自于 Clock 的 state,或是 Clock 的 props,还是手动输入的。

这通常会被叫做“自上而下”或是**“单向”的数据流**。任何的 state 总是所属于特定的组件,而且从该 state 派生的任何数据或 UI 只能影响树中“低于”它们的组件。

如果你把一个以组件构成的树想象成一个 props 的数据瀑布的话,那么每一个组件的 state 就像是在任意一点上给瀑布增加额外的水源,但是它只能向下流动。

# 每个组件都是真正独立的。

编辑 (opens new window)
#React
上次更新: 2023/7/11 18:57:41
组件&Props
事件处理

← 组件&Props 事件处理→

最近更新
01
IoC 解决了什么痛点问题?
03-10
02
如何调试 Nest 项目
03-10
03
Provider注入对象
03-10
更多文章>
Copyright © 2019-2025 Study | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式