了解一个React组件

转载 2016年08月31日 13:33:22



从一个简单的组件开始

标题叫“一个简单的组件”,于是先来看下官方网站上一个叫做“A Simple Component”的例子来感受下:

var HelloMessage = React.createClass({
  render: function() {
    return <div>Hello {this.props.name}</div>
  }
})
React.render(<HelloMessage name="John" />, mountNode)

好的,通过React提供的工厂方法,我们创建了一个组件,在这里,我们认为createClass创建了一张blueprint,而通过jsx语法创建的<HelloMessage name="John" />则返回一个ReactElement对象来具体地告诉React要如何渲染组件(因为除了blueprint以外还需要一些外部状态的传入),这等同于调用React.createElement(HelloMessage, { name: 'John' }, 'Hello ', this.props.name)

之后,React就可以根据ReactElement对象,把组件挂载到页面的某个节点上去,也就是React.render方法在做的事情,这个方法返回一个组件的实例,同时也意味着你可以在已有项目中的一小部分尝试React。

下面来具体介绍下。

JSX

上面的例子里看着像是把HTML写在了Js里,实际上是通过一种更加清晰易读且易维护的语法JSX来创建ReactElement对象。对React来说JSX是可选的,如果真的不喜欢也可以用React.createElement这个api。

当然有JSX语法的js文件是不能直接在浏览器中运行的,我们可以用官方的JSXTransformer或者是babel来转换。

需要注意的是classfor这两个HTML属性,由于组件的属性实际是以对象形式传递的,比如上面的{ name: 'John' },另外js不允许关键字作为属性名,所以需要分别用classNamehtmlFor代替。

创建一个组件

创建组件的时候需要给React.createClass提供一个对象,这个对象必须包含一个render方法和若干可选的生命周期方法。

要注意的是我们需要保证render函数是纯函数,即同样的输入始终返回相同的输出,并且执行过程中没有副作用(和DOM交互或者发Ajax请求)。但一个组件要和DOM交互或者发Ajax请求需求是很正常的,那么就要用到其他生命周期方法了。

除此之外,更重要的部分是,让一个组件可以工作除了有blueprint外,还需要组件状态。对于一个React组件来说,分为不可变状态this.props和可变状态this.states

我们可以通过this.props决定一个组件内的部分呈现内容,比如上例中我们希望呈现的名字是John,且不会改变。然而对于一个DropDownList而言,仅在点击它时,一个下拉列表才会显示出来,那么我们认为这个下拉列表是否显示就是一个可变状态。

说到可变状态,那么要这么变?React并不希望我们直接修改this.states,我们需要使用this.setState的方式修改状态,因为每次调用this.setStaterender方法都会被再次调用,同时也会调用一些相关的生命周期函数。this.setState接受一个对象作为新状态的patch,也就是说这个对象不会覆盖现有的this.states,而是一个类似extend的行为。

我们也可以提供一些默认状态:

getDefaultProps () {
  return {
    name: 'defaultName'
  };
},
getInitialState: function() {
  return {
    listShowed: false
  };
},

其中getDefaultProps仅会被调用一次,这里的意思是无论你会创建多少个ReactElement,这个函数都只执行一次,之后的默认props都会直接使用改函数的返回值。

这里需要提一下React提供以ES6的方式创建组件,有意思的是ES6的版本用的是React.Component,在语意上比createClass更加明确。getDefaultPropsgetInitialState在ES6的版本中有些不太一样,相对与getDefaultProps,ES6将默认属性对象作为了构造函数的一个属性,而getInitialState则变成了在其构造器函数中给this.state赋值,来看一个栗子:

class HelloMessage extends React.Component {
  constructor (props, context) {
    super(props, context)

    this.state = {  } // 初始化状态
  }
  render () {
    return <div>Hello {this.props.name}</div>
  }
}
HelloMessage.defaultProps = { name: 'defaultName' }

组件的生命周期

之前的部分一直有提到生命周期函数,下面就来介绍下:

componentWillMount会在组件即将被挂载时调用,此时this.refs对象为空对象。如果在该函数中使用this.setState,那么会更新this.states对象,而render依然只会调用一次,相当于是可以覆盖getInitialState返回的对象,虽然我觉得这没什么意义。

componentDidMount是非常常用的生命周期方法,仅当组件被挂载后调用一次,这意味着可以在这个函数中进行一些DOM操作等,比如希望组件中的一个textbox可以再挂载后自动获取焦点:

componentDidMount () {
  const textbox = React.findDOMNode(this.refs.text)
  if (this.props.autoFocus) textbox.focus()
}

componentWillReceiveProps在将要接受新的props时被调用,不是说props是不可变状态吗?情况通常是这样的,当一个父组件包含了一个子组件,子组件的一个props的值是父组件的states的值,那么当父组件可变状态改变时,子组件的props也更新了,于是调用了这个函数。

componentWillReceiveProps (nextProps) {
  if (this.props.disabled !== nextProps.disabled) {
    // disabled这个属性改变了
  }
}

这个生命周期函数componentWillReceiveProps提供了更新states的机会,可以调用this.setState,也是唯一可以在组件更新周期中调用this.setState的函数。

shouldComponentUpdate是在更新前根据该函数的返回值决定是否进行这次更新。

shouldComponentUpdate (nextProps, nextState) {
    // 比较props或者states,返回true则更新照常,返回false则取消更新,且不会调用下面的两个生命周期函数
}

考虑这种情况:父组件有子组件A和子组件B,当父组件调用this.setState更新一个作为子组件A属性的state时,render方法被再次调用,此时组件A和组件B同时被更新,其实真正改变的只有组件A,但组件B也同时被要求更新了,这是没有必要的,于是shouldComponentUpdate就显的有用了,在该函数体内比较props或是states,如果没有改变就取消这个更新,这对性能上算是一个提升。

但如果是复杂对象的比较就比较麻烦了,因为我们无法通过===来判断两个对象的键值是否都相等,于是我们就希望我们的对象是不可变的(immutable)。这里不再展开了,大家可以先自行探索,之后再写这个部分的文字,并介绍immutable.js

componentWillUpdate在组件被更新前调用一次,可以用来做一些更新前的准备工作,举个栗子:

componentWillUpdate (nextProps, nextState) {
  if (!this.props.isShowed && nextProps.isShowed) {
    // 比如下拉菜单此时变的显示了,可以对此监听一些事件什么的
  }

  if (this.props.isShowed && !nextProps.isShowed) {
    // 比如下拉菜单隐藏了,可以在这里取消事件监听
  }
}

(组件更新前事情还蛮多的...)

componentDidUpdate在组件更新完成后调用,可以考虑在这个函数中执行一些DOM操作什么的。

注意:绝对不要在componentWillUpdatecomponentDidUpdate中调用this.setState方法,否则将导致无限循环调用。

componentWillUnmount会在组件即将从挂载点移去时调用,此方法专门用来『擦屁股』,比如去除即将被销毁的DOM节点的引用,或者是清除计时器,取消监听的时间等等。



文/leozdgao(简书作者)

React源码分析2 — 组件和对象的创建(createClass,createElement)

1 组件的创建React受大家欢迎的一个重要原因就是可以自定义组件。这样的一方面可以复用开发好的组件,实现一处开发,处处调用,另外也能使用别人开发好的组件,提高封装性。另一方面使得代码结构很清晰,组件...
  • u013510838
  • u013510838
  • 2017年02月18日 18:44
  • 2959

ReactJS读书笔记四:mixins

前面说过,react使用组合而不是继承来处理父子组件。 那么显然无法通过继承父类来实现公共功能,所以react提供了mixin的机制。 一 mixin是什么 mixin,可以非常简单的理解,他...
  • lihongxun945
  • lihongxun945
  • 2015年06月08日 20:00
  • 9298

初识reactJS的组件化开发(一):简单封装

var navBar = React.createClass({ render: function (){ return {nav_li} } })createClas...
  • github_26672553
  • github_26672553
  • 2016年07月30日 15:42
  • 4496

react组件开发流程(一个新手的入门开发整理)

  • 2017年07月25日 09:00
  • 101KB
  • 下载

如何创建一个依赖Android AAR文件的React Native组件

在React Native模块项目中如何使用AAR文件?官网没有相关信息。只能通过Google,StackOverflow和GitHub去寻找答案。这里分享下如何使用Dynamsoft提供的barco...
  • yushulx
  • yushulx
  • 2017年04月25日 15:07
  • 637

【ReactJS】通过一个例子学习React组件的生命周期

源代码
  • u013451157
  • u013451157
  • 2018年01月02日 18:42
  • 142

如何创建一个android的react-native组件(二)

接着上一篇文章,这一篇我把自己上传到npm上的react-native-segmented-android开发步骤和大家分享。 下载react-native组件命令:$ npm install re...
  • zzyyppqq
  • zzyyppqq
  • 2016年02月28日 22:08
  • 1260

用React+Mobx到底该不该写一个CheckBox组件

从接触React到现在有几个月了,对React的理解也在不断的加深。 最开始还是在拿React当工具用,嗯,就是比模板引擎牛逼点的工具。 后来觉得React在颠覆,颠覆自己对html的认识,或者说我更...
  • shuzipai
  • shuzipai
  • 2017年11月06日 15:36
  • 198

react入门——实现一个输入框组件

React组件化开发初试 按照官方文档和例程博客,实现了一个简单的输入框组件。 如果想了解官方案例,请参考深入理解 React 总结一下,一个简单的React.js应用应按照以下步骤构建: ...
  • hc1025808587
  • hc1025808587
  • 2016年06月28日 10:35
  • 9198

如何创建一个Android原生的react-native组件(一)

本人是android开发者,刚刚接触react-native不到一月,这是我的第一篇原创博客,从一开始照着React Native上文档的例子熟悉每一个控件的使用,到页面跳转,跳转到android原生...
  • zzyyppqq
  • zzyyppqq
  • 2015年12月18日 10:12
  • 716
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:了解一个React组件
举报原因:
原因补充:

(最多只允许输入30个字)