文章标题总算是可以正常一点了……
通过之前的文章我们已经知道:在 React 体系中所谓的 "在 JavaScript 中编写 html 代码" 指的是 React 扩展了 JavaScript 的语法,也就是 JSX。JSX 语法中可以以类似 HTML 语法的方式使用 React 组件,从而编写 React 组件就有一种创造一个新的 HTML 标签的体验。
上一篇文章《玩转 React(四)- 创造一个新的 HTML 标签》介绍了如何来创建一个 React 组件,以及组件的属性。了解到组件的视图是属性的映射,通过改变组件属性可以触发组件重新渲染,从而改变组件的视图。其实组件的视图并不仅仅是由属性映射来的,本篇将介绍另一种可以触发组件重新渲染的方式,即组件的内部状态(state),严格来说组件的视图是由属性和内部状态映射而来的,即:
,跟属性类似,状态的改变也会触发组件重新渲染,只不过状态是组件内部基于自身逻辑或者用户事件自己维护的,而不是由外部输入的。
- view = f(props, state)
另外本文中会介绍一个通过类继承方式定义的组件的生命周期,以及在各个生命周期函数中能做什么,不能或尽量不要做什么。
在一个单页面 web 应用中通常只调用一次。
- ReactDOM.render
改变内部状态
- setState
来更新视图。
- state
多数情况下是异步的。
- setState
的值生成下一个
- state
。
- state
修改
- this.state
。
- state
以上是本文的内容摘要,如果你已经知道我要说的是什么,那么就没有必要继续看下去了,节约时间。
此前,我们已经了解到可以通过
的方式,将带有特定属性的组件渲染到页面的某个 DOM 节点中(container),这样页面上会展示出 “Hello Lucy”,当我们希望页面上展示 “Hello Tom” 的时候,我们可以将组件的 name 属性改为 Tom 后再次调用
- ReactDOM.render(<HelloMessage name="Lucy" />, container)
方法,这样组件就会以新的属性重新渲染,从而更新组件的视图。
- ReactDOM.render
下面是官方文档中一个展示时钟的例子,我简单改造了下:
codepen.io/Sarike/pen/…
例子中定义了一个
组件,组件接收一个
- Clock
属性,在组件外部通过
- time
周期性地调用
- setInterval
不断更新
- ReactDOM.render
的属性并重新渲染。
- Clock
然而在很多实际场景中,对于一个时钟组件,我们希望它有更好的封装性和复用性,也就是说我们希望只调用一次
然后它可以自己更新自己的视图,这样我们就更容易在页面上放置多个时钟了,即复用性更好了。
- ReactDOM.render(<Clock />, container)
要达到这个目的,就需要组件的内部状态来支持。组件有一个特殊的属性
用来保存组件的内部状态。用户可以通过
- state
来更新组件的状态,组件的状态更新后会重新执行
- this.setState(statePatch)
方法来更新视图,上面的例子使用内部状态改造后:
- render
codepen.io/Sarike/pen/…
这样
作为一个完整的时钟组件就可以自己来更新自己了,上篇文中也有提到过,如果想要使用组件的内部状态,那组件必须以类继承的方式来定义,而不能使用函数式组件。所以说,函数式组件经常也被称作是无状态组件(stateless)。
- Clock
上面例子中有用到
和
- componentDidMount
两个函数,它们是组件的生命周期函数,本文的后半部分将会介绍,这俩函数分别在组件挂载到页面上和组件将要从页面上移除时调用。
- componentWillUnmount
改造后的例子,我们只需要调用一次
即可,在实际的项目中,一个完整的单页面 web 应用,也只需要调用一次
- ReactDOM.render
方法把根组件挂载到页面中即可,剩下的工作就都放心地交给 React 就行了。
- ReactDOM.render
在创建一个拥有内部状态的组件时,我们需要对内部状态进行初始化,即设置组件最初的状态是什么。做法很简单,就是在构造函数
中设置
- constructor
属性就可以了。如下所示:
- state
- class MyComponent extends React.Component {
- constructor(props) {
- super(props); // 这行代码不能少哦
- this.state = {
- name: "Lucy"
- }
- }
- }
多数情况下是异步的,异步意味着通过
- setState
更新组件状态后,不能立刻通过
- setState
来获取到更新之后的值,另外当连续多次调用
- this.state
来更新同一个字段时,只有最后一次更新才会生效。如下示例:
- setState
codepen.io/Sarike/pen/…
如果希望上面示例代码正常工作,你需要通过回调函数的方式来生成下一个 state,如下所示:
- this.setState(preState => ({value: preState.value + 1}));
- this.setState(preState => ({value: preState.value + 2}));
- this.setState(preState => ({value: preState.value + 3}));
所以,直接基于当前
的值,生成一下个
- state
是不靠谱的,但是很多不清楚这一点的同学基本上都是这么做的,因为写起来简单嘛,而且貌似也没有什么问题。这是因为很多情况下,业务逻辑没有那么复杂,基本不会频繁调用
- state
。但是这确实是一个隐患,如果在项目初期不注意规避,等项目复杂到一定程度以后,可能会出现难以排查的BUG。
- setState
那为什么说多数情况下是异步的呢?难道有些情况下不是异步的吗?是的,实际上只有在 React 能控制的事件处理过程中调用的
才是异步的,如:生命周期函数,React 内置的如 button,input 等组件的事件处理函数。在多数的情况下我们只需要在这些地方控制我们的组件就够了,所以说大多数情况下
- setState
是异步的。
- setState
在某些特殊的组件中,可能需要通过
来设置某些 DOM 的事件处理函数,在这种通过原生的 JS API 来设置的事件处理过程调用
- addEventListener
就是同步的,会立即更新
- setState
。另外还有
- this.state
、
- setInterval
等原生 API 的回调函数也是如此。
- setTimeout
参考:www.zhihu.com/question/66…
这一点跟属性类似,直接通过
修改组件状态,组件状态被修改了,但并不会触发组件的重新渲染。这样就会导致组件视图与状态不一致。
- this.state
一个组件被我们创造到这个世界上之后,在使用它时,它的每个实例都是有一定生命周期的,下面这张图说明了一个组件实例的生命周期:
- componentWillReceiveProps(nextProps)
- shouldComponentUpdate(nextProps, nextState)
- componentDidUpdate(prevProps, prevState)
- componentDidCatch(error, info)
来源: https://juejin.im/post/59f6bd386fb9a0451e3f119c