Components and Props(组件和属性对象)

Components(组件)把你的用户界面分解每个独立的部分,这样就可以复用,保持作用域隔离等等!

概念性地理解,Components就好像javascript里面的function一样,它可以接收任意的数据作为输入(props),同时最后返回一个用以决定你的界面上显示什么内容的React elements。

Functional and Class Components(函数式组件和类式组件)

最简单的方法创建一个React component就是使用javascript的function,代码如下:

  1. function Welcome(props){
  2. return <h1>Hello,{props.name}</h1>;
  3. };

我们说上面的Welcome是一个有效的React Component,是因为它接收一个props作为参数,并且返回一个React element。我们说这是一个函数式组件,是因为它就是以javascript的function创建的。

你也可以用es6 class创建一个类式组件,如:

  1. class Welcome extends React.Component{
  2. constructor(props){
  3. super(props);
  4. };
  5. render(){
  6. return <h1>Hello,{this.props.name}</h1>;
  7. };
  8. };

从React视图上来说,上面两个组件是等价的。

但是类式创建react component会有一些附加的属性用以更灵活的控制components,我们将在下一个章节讨论,但是在此之前,为了更简洁的说明问题,我们将用函数式Components。

Rendering a Component(渲染一个组件到页面)

在此之前,我们总是渲染的标签是一个标准的dom标签:

  1. const element = <div />;

然而,elements也可以是一个用户定义的组件:

  1. const element = <Welcome name="Sara" />;

当react看到一个用户定义的组件时,它将会传递一个“props”对象给它!

看一个例子,下面的代码将会页面上显示“Hello,Sara”。

  1. function Welcome(props){
  2. return <h1>Hello,{props.name}</h1>;
  3. };
  4. const element = <Welcome name="Sara" />;
  5. ReactDOM.render(
  6. element,
  7. document.getElementById('root')
  8. );

我们简要说明一下,上面的代码发生了什么:

_ 1、我们通过调用ReactDOM.render,渲染了 element;

_ 2、React将对象{name:’Sara’}作为props参数传递给Welcome组件;

_ 3、组件Welcome返回<h1>Hello,Sara</h1>结果作为一个element;

_ 4、React有效地更新DOM,以此来跟<h1>Hello,Sara</h1>匹配。

  • 警告

组件名字的第一个字母通常是大写字母。

例如,一个<div />代表一个DOM tag,但是代表一个组件,同时需要把Welcome添加到作用域中。

Composing Components (构造组件)

在react中,一个组件的输出通常可以依赖于其它组件,这使得你可以使用组件来描述任何页面内容,比如:一个按钮、一个表单,一个对话框,一屏内容等;所有这些通常都可以表达为一个组件。

例如,我们可以创建一个App component ,它内部多次引用了Welement组件,如:

  1. function Welcome(props){
  2. return <h1>Hello,{props,name}</h1>;
  3. };
  4. function App(){
  5. return (
  6. <div>
  7. <Welcome name="Sara"/>
  8. <Welcome name="Cahal"/>
  9. <Welcome name="Edite"/>
  10. </div>
  11. );
  12. };
  13. ReactDOM.render(
  14. <App />,
  15. document.getElementById('root')
  16. );

通常情况下,一个新建的React App在顶层只有一个App组件,然而,如果你是集成react到一个已存在的app里面,你将会自下而上的创建一些小组件,比如一个按钮组件等等,然后逐步到顶层中。

  • 警告

react components只能有一个根元素,所以我们创建App组件的时候会使用一个div把所有的Welcome组件包起来。

Extracting Components(分离、提取组件)

不要害怕去把一个复杂、庞大的组件分离层一些小的组件。

例如,我们看一下下面的评论component:

  1. function Comment(props){
  2. return (
  3. <div className="Comment">
  4. <div className="UserInfo">
  5. <img className="Avatar"
  6. src={props.author.avatarUrl}
  7. alt={props.author.name}
  8. />
  9. <div className="UserInfo-name">
  10. {props.author.name}
  11. </div>
  12. </div>
  13. <div className="Comment-text">
  14. {props.text}
  15. </div>
  16. <div className="Comment-date">
  17. {formatDate(props.date)}
  18. </div>
  19. </div>
  20. );
  21. };

首先我们看一下这个组件描述的UI需要那些数据(props):包含一个author对象、一个字符串text、一个日期字符串。它描述了一个在社交网站上评论板块内容。

这个组件首先很难去改里面数据,因为嵌套了很多层,同时它很难复用代码,因为组件每部分的耦合性太强了,是连在一起的,我们对它进行适当的提取来改变这种情况。

首先,我们将提取一个Avatar组件:

  1. function Avatar(props){
  2. return (
  3. <img className="Avatar"
  4. src={props.user.avatarUrl}
  5. alt={props.user.name}
  6. />
  7. );
  8. };

这个Avatar是一个独立的组件,所以它并不需要知道它将在用在Comment组件里面,这也是为什么我们给了一个更通用的user属性名,而不是auther。

我们建议在给一个组件取属性名的时候,名字应该根据这个它的功能来取,而不需要根据它将使用的上下文环境来命名。

现在我们就可以简化上面的Comment组件一点点了,代码如:

  1. function Comment(props){
  2. return (
  3. <div className="Comment">
  4. <div className="UserInfo">
  5. <Avatar user={props.author}/>
  6. <div className="UserInfo-name">
  7. {props.author.name}
  8. </div>
  9. </div>
  10. <div className="Comment-text">
  11. {props.text}
  12. </div>
  13. <div className="Comment-date">
  14. {formatDate(props.date)}
  15. </div>
  16. </div>
  17. );
  18. };

下一步,我们将提取UserInfo组件,代码如下:

  1. function UserInfo(props){
  2. return (
  3. <div className="UserInfo">
  4. <Avatar user={props.user}/>
  5. <div className="UserInfo-name">
  6. {props.user.name}
  7. </div>
  8. </div>
  9. );
  10. };

现在,我可以进一步简化我们的comment组件:

  1. function Comment(props){
  2. retrun (
  3. <div className="Comment">
  4. <UserInfo user={props.author}/>
  5. <div className="Comment-text">
  6. {props.text}
  7. </div>
  8. <div className="Comment-date">
  9. {formatDate(props.date)}
  10. </div>
  11. </div>
  12. );
  13. };

提取组件这项工作一开始总是很繁重的工作,但是你这样做了的话,那么将有利于你后期复用代码,更利于构建大型的app。一个良好的经验法则:如果你写的一个组件包含了很多概念上能却别开来独立的部分,比如,一个对话框里面分别包含了标题,按钮,文字等等;或者,你写的组件非常庞大,那么可以确信,你的组件非常需要进一步提取小组件。

Props are Read-Only(props是只读的,不要尝试修改它的值)

无论你是采用函数式还是类式创建组件,都不能修改的它的props值,来看一下下面的sum函数:

  1. function sum(a,b){
  2. return a + b;
  3. };

我们称这样的函数为pure函数,因为他不会企图修改的输入值,而且同样的参数,总是返回同样的值。

作为对比,下面的函数就是不纯的,因为它修改了自己的输入参数:

  1. function withdraw(account,amount){
  2. account.total -= amount;
  3. };

react是非常灵活的,但是它有一条非常严格的规则:

一个组件必须扮演成一个pure的函数,它会尊重它的props值,不要尝试修改props值。

当然了, 应用程序用户界面是动态的和时刻在变的,在下一个章节,我们将介绍另一个概念”state”,state就不遵守这条规则,state可以允许你在任何时间修改它的输出值。