首页 > 技术文章 > 设计react组件 props propTypes

mawn 2018-05-21 09:57 原文

首先作为一个合格的开发者,不要只满足于编写出了可以运行的代码,而耍了解代码背 后的工作原理;不要只满足于自己编写的程序能够运行,还要让自己的代码可读而且易 于维护 。 这样才能开发出高质量的软件 。

易于维护组件的设计要素

作为软件设计的通则,组件的划分要满足高内聚(High Cohesion)和低耦合(Low Coupling)的原则 。

高内聚指的是把逻辑紧密相关的内容放在一个组件中 。 用户界面无外乎内容 、 交互 行为和样式 。 传统上,内容由 HTML 表示,交互行放在 JavaScript代码文件中,样式放 在 css 文件中定义 。 这虽然满足一个功能模块的需要,却要放在三个不同的文件中,这 其实不满足高内聚的原则 。 React却不是这样,展示内容的 JSX、定义行为的 JavaScript 代码,甚至定义样式的 css,都可以放在一个 JavaScript文件中,因为它们本来就是为了实现一个目的而存在的,所以说 React天生具有高内聚的特点 。

低耦合指的是不同组件之间的依赖关系要尽量弱化,也就是每个组件要尽量独立 。保持整个系统的低耦合度,需要对系统中的功能有充分的认识,然后根据功能点划分模 块,让不同的组件去实现不同的功能,这个功夫还在开发者身上,不过, React 组件的对 外接口非常规范,方便开发者设计低祸合的系统 。

 

react组件的数据

俗话说的好  差劲的程序员操心代码,优秀的程序员操心数据结构和它们之间的关系

React组件的数据分为两种, prop和 state,无论 prop或者 state 的改变,都可能引发组件的重新渲染,那么,设计一个组件的时候,什么时候选择用 prop什么时候选择用state 呢?其实原则很简单, prop 是组件的对外接口, state 是组件的内部状态,对外用prop,内部用 state。

React 的 prop

在 React 中, prop ( property 的简写)是从外部传递给组件的数据, 一个 React 组件 通过定义自己能够接受的 prop 就定义了自己的对外公共接口 。

每个 React组件都是独立存在的模块,组件之外的一切都是外部世界,外部世界就是通过 prop 来和组件对话的 。

 

我们先从外部世界来看, prop是如何使用的,在下面的JSX代码片段中,就使用了prop:

<SampleButton
   borderWidth={2} onClick={onButtonClick} style={{ color: "red" }}
 />

在上面的例子中,创建了名为 SampleButton 的组件实例,使用了名字分别为 borderWidth、 onClick 和 style 的 prop,看起来, React 组件的 prop 很像是 HTML 元素的属性, 不过, HTML 组件属性的值都是字符串类型,即使是内嵌 JavaScript,也依然是字符串形 式表示代码。 React组件的 prop所能支持的类型则丰富得多,除了字符串,可以是任何 一种 JavaScript语言支持的数据类型。

比如在上面的 SampleButton 中, borderWidth 就是数字类型, onClick是函数类型, style 的值是一个包含 color字段的对象,当 prop 的类型不是字符串类型时,在 JSX 中必 须用花括号{}把 prop 值包住,所以 style 的值有两层花括号,外层花括号代表是 JSX 的 语法,内层的花括号代表这是一个对象常量。

当外部世界要传递一些数据给 React组件,一个最直接的方式就是通过 prop ;同样,React组件要反馈数据给外部世界,也可以用 prop,因为 prop 的类型不限于纯数据,也 可以是函数,函数类型的 prop等于让父组件交给了子组件一个回调函数,子组件在恰当 的实际调用函数类型的 prop,可以带上必要的参数,这样就可以反过来把信息传递给外 部世界.

看下面代码

class ControlPanel extends Component { 
 render () {
    return (
      <div>
         <Counter caption=”First” initValue={O} /> 
         <Counter caption=” Second" initV alue={lO} />
         <Counter caption=”Third” initValue={20} />
      </div>
             );
       )
     )

对于 Counter 组件,父组件 Contro!Panel 就是外部世界,我们看 Contro!Panel 是如何 用 prop 传递信息给 Counter 的,

Contro!Panel组件包含三个 Counter组件实例 在 ControlPanel 的 render 函数中将这 三个子组件实例用 div包起来,因为 React要求 render函数只能返回一个元素。

在每个 Counter组件实例中,都使用了 caption 和 initValue两个 prop。 通过名为 ca严ion 的 prop, ControlPanel传递给 Counter组件实例说明文字。 通过名为 initValue 的 prop传递给 Counter组件一个初始的计数值 。

读取 prop值

我们再来看 Counter组件内部是如何接收传人的 prop 的,首先是构造函数,代码 如下:

class Counter extends Component { 
    constructor(props) {
        super (props) ;
        th工s.onClickincrementButton = this.onClickincrementButton.bind(this);
        this.onClickDecrementButton = this.onClickDecrementButton.bind(this);         
        this.state= {
           count: props.initV alue I I 0        
             }
        }

  

如果一个组件需要定义自己的构造函数,一定要记得在构造函数的第一行通过 super 调用父类也就是 React.Component 的构造函数 。 如果在构造函数中没有调用 super(props), 那么组件实例被构造之后,类实例的所有成员函数就无法通过 this.props访问到父组件传 递过来的 props值。很明显,给 this.props赋值是 React.Component构造函数的工作之一。

在 Counter 的构造函数中还给两个成员函数绑定了当前 this 的执行环境,因为 ES6 方法创造的 React组件类并不自动给我们绑定 this到当前实例对象。

在构造函数的最后,我们可以看到读取传入 prop 的方法,在构造函数中可以通过参 数 props 获得传人 prop 值,在其他函数中则可以通过 this.props 访问传人 prop 的值,比如在 Counter 组件的 render 函数中,我们就是通过 this.props 获得传人的 caption, 函数代码如下:

const{ caption }=this.props

在上面的代码中,我们使用了 ES6 的解构赋值( destructuring assignment)语法从 this. props 中获得了名为 caption 的 prop 值 。

propTypes检查

在 ES6 方法定义的组件类中,可以通过增加类的 propTypes 属性来定义 prop 规格, 这不只是声明,而且是一种限制,在运行时和静态代码检查时,都可以根据 propTypes 判断外部世界是否正确地使用了组件的属性 。

比如,对于 Counter组件的 propTypes定义代码如下:

import PropTypes from 'prop-types'
 
ClickCounter.propTypes={
 caption: PropTypes.string.isRequired,
}

其中要求 caption必须是 string类型, initValue必须是 number类型 。 可以看到,两 者除了类型不同之外,还有一个区别: caption 带上了 isRequried,这表示使用 Counter 组件必须要指定 caption;而 initValue 因为没有 isRequired,则表示如果没有也没关系 。

为了验证 propTypes 的作用,可以尝试故意违反 propTypes 的规定使用 Counter实 例,

 

 

 

推荐阅读