转载

《ReactJS》读书笔记六

组件的复合

和传统的 HTML 页面是由元素组成的一样,在 React 中,构建页面的基础单元是 React 组件。React 组件就好比混入了 JavaScript 表达能力的 HTML 元素。实际上写 React 代码主要是在构建组件,而构建组件就像编写 HTML 文档时使用元素一样。

本质上,一个组件就是一个 JavaScript 函数,它接受 props 和 state 作为参数,并输出渲染好的 HTML。你也可以把 React 组件理解为 HTML 元素的扩展。

扩展 HTML

React + JSX 是强大而富有表现力的工具,允许我们使用类似 HTML 的语法创建自定义元素。比起传统的 HTML,这些自定义元素还能够控制生命周期中的行为。

相比较于继承,React 更-偏向于复合,也就是通过结合小巧的、简单的组件和数据对象,构造大而复杂的组件。正如在构建网页时不会扩展 HTML DOM 节点那样,React 组件是不可以扩展的,而是通过组件之间的组合来构建复杂的应用的。

组件符合的例子

一个渲染选择题的组件要满足以下几个条件:

  • 接收一组选项作为输入。
  • 把选项渲染给用户。
  • 只允许用户选择一个选项。

HTML 提供了一些基本的元素 —- 单选类型的输入框和表单组,可以在这里使用。组件的层级从上往下看是这样的:

MultipleChoice -> RadioInput -> Input (type="redio"); 

组装 HTML

我们从下网上组装这个组件。先建立一个脚手架,其中包含所需的渲染方法和基本的标记,用以描述想输出的界面。组合模式开始显现,组件变成了特定类型的输入框:

var AnswerRadioInput = React.createClass({  render: function() {   return (    <div className="radio">     <label>      <input type="radio" />     </label>    </div>   );  } }); 

添加动态属性

现在 input 还没有内容是动态的,所以下一步需要定义父元素必须传给单选框的那些属性:

  • 这个输入框代表什么值或者选项?(必填)
  • 用什么文本来描述它?(必填)
  • 这个输入框的 name 是什么?(必填)
  • 也许需要自定义 id
  • 也许要重载它的默认值。

有了上述的描述后我们就可以开始自定义 input 的属性类型了。我们把这些添加到类的 PropTypes 对象中:

var AnswerRadioInput = React.createClass({  proTypes: {   id: React.PropTypes.string,   naame: React.PropTypes.string.isRequired,   label: React.PropTypes.string.isRequired,   value: React.PropTypes.string.isRequired,   checked: React.PropTypes.bool  },  ... }); 

对于每个非必须的属性我们都应该为其定义一个默认值。把它们添加到 getDefaultProps() 方法中。在每个新的实例中,如果伏组件没有传递数据给它们,这些定义的默认值就会被使用。

由于这个发那个发只会在类上 调用一次 ,而不是在每个实例上都调用,因此不能在这里提供 id —- 每个实例应该保持 id 的唯一性。

前面的笔记中有提到过对于变化的数据,应该用 state 来存放,所以我们应该把 id 的值放在 state 中。

var AnswerRadioInput = React.createClass({  proTypes: {...},  getDefaultProps: function() {   return {    id: null,    checked: false   };  },  ... }); 

状态追踪

我们的组件需要记录随时间而变化的数据。尤其是对于每个实例来说都要求是唯一的 id ,以及用户可以随时更新的 checked 值:

var AnswerRadioInput = React.createClass({  proTypes: {...},  getDefaultProps: function() {...},  getInitialState: function() {   var id = this.props.id ? this.props.id : uniqueId('radio-');   return {    checked: !!this.props.checked,    id: id,    name: id   };  },  ... }); 

现在你可更新渲染标记,获取新动态的状态和属性了:

var AnswerRadioInput = React.createClass({  proTypes: {...},  getDefaultProps: function() {...},  getInitialState: function() {...},  render: function() {   return (    <div className="radio">     <label htmlFor={this.props.id}>      <input        type="radio"       name={this.props.name}       id={this.state.id}       checked={this.state.checked}      />      {this.props.label}     </label>    </div>   );  },  ... }); 

整合到父组件当中

目前这个组件已经足够完善了,可以把它应用到一个父组件中了。接下来我们来构建下一层 —- AnswerMultipleChoiceQuestion 。这一层的组要作用是渲染一列选项让用户从中选择。同样按照上面的方法,可以先创建这个组件基本的 HTML 和默认属性:

// Page 48. 

为了生成一列单选框子组件,我们需要对选项列表进行映射,把每一个项转化为一个组件:

// Page 48. 

现在 React 的可组合性显得更清晰了。从一个通用的输入框开始,将其定制为一个单选框,最终将其封装进一个选择题组件 —- 一个高度定制具备特定功能的表单控件。现在渲染一列选项就简单了:

<AnswerMultipleChoiceQuedtion choices={arrayOfChoices} ... /> 

看到这里你可能会觉得有点怪怪的 —- 单选框怎么把自身的变化通知给父组件呢?

父组件、子组件关系

此时我们已经可以把一个表单组件渲染到页面上了,不过此时子组件还没有能力与父组件进行通信。

要如何使得父子组件可以通信呢?最简单的方法就是使用 props 属性。父组件可以通过属性传入一个回调函数,子组件在需要时进行调用。

首先需要定义一个 AnswerMultipleChoiceQuestion 在其子组件变更后要做什么。添加一个 handleChanged() 方法然后把它传递给所有的 AnswerRadioInput 组件:

var AnswerMultipleChoiceQuestion = React.createClass({  ...,  handleChanged: function() {   this.setState({    value: value   });   this.props.onCompleted(value);  },  renderChoices: function() {   return this.props.choices.map(function() {    return AnswerRadioInput({     ...,     onChanged: this.handleChanged    });   }).bind(this);  },  ... }); 

现在需要让每个单选框监听用户的更改,然后把数值向上传递给父组件。这需要将一个事件处理函数关联到输入框的 onChange 事件上:

// Page 51. 

总结

所以,编写 React 实际上就是在编写 React 的组件,而编写 React 组件又实际上是在编写 HTML。然后 React 父子组件可以通过 props 属性进行简单通信。

原文  http://justclear.github.io/developing-a-react-edge-notes-for-part-6
正文到此结束
Loading...