Components和Props

作者: 莫铭 | 来源:发表于2017-04-25 23:42 被阅读42次

    Components使得UI被划分为一个个独立、可复用的零件,并单独考虑每个零件。
    在概念上,组件就像是JavaScript函数。他们接受任意的输入(称为"props"),并返回React elements来描述屏幕上应该出现的内容。

    函数式Components和class Components

    定义一个Component最简单的方法就是写个JavaScript函数:

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

    这个函数是一个有效的React component,因为他接受一个名为'props'对象的参数,并且返回一个React element。我们称这种组件为'函数式'的,因为他们就是普通的JavaScript函数。
    你也可以用一个ES6 class来定义component:

    class Welcome extends React.Component {
      render() {
        return <h1>Hello, {this.props.name}</h1>;
      }
    }
    

    从React的角度来看,上面这两个components是相同的。
    我们将在下一节讨论,Classes方式中的一些额外特性。在那之前,方便起见,我们将使用函数式的components。

    渲染Component

    之前,我们至于见过表现DOM标签的React elements:

    const element = <div />;
    

    不仅如此,elements也可以表示用户自定义的components:

    const element = <Welcome name="Sara" />
    

    当React发现一个表示用户自定义组件的element,它会把JSX属性作为一个单独的对象传给这个component。我们将这个对象成为'props'。
    举个例子,下面的代码在页面上渲染出"Hello, Sara":

    function Welcome(props) {
      return <h1>Hello, {props.name}</h1>;
    }
    
    const element = <Welcome name="Sara" />;
    ReactDOM.render(
      element,
      document.getElementById('root')
    );
    

    在CodePen上试试看
    让我们重新过一遍这个例子中发生了什么:

    1. 我们调用ReactDOM.render()函数去渲染element:<Welcome name="Sara" />
    2. React调用Wecome组件,将{name: 'Sara'}作为props。
    3. 我们的Welcome组件返回一个element: <h1>Hello, Sara</h1>作为结果。
    4. React DOM高效地更新DOM来匹配<h1>Hello, Sara</h1>

    警告:
    component名字的首字母始终都是大写。
    比如,<div />表示一个DOM标签,但是<Welcome />表示一个component,而且需要将Welcome加入当前范围内。

    组合Components

    Components可以在他们的输出中引用其他的components。这样我们就可以使用相同的组件来叠加完成任意的细节。按钮,表单,对话框和屏幕,在React应用中,这些通常都表示为组件。
    比如:我们可以创建一个App组件用来渲染多个Welcome

    function Welcome(props) {
      return <h1>Hello, {props.name}</h1>;
    }
    
    function App() {
      return (
        <div>
          <Welcome name="Sara" />
          <Welcome name="Cahal" />
          <Welcome name="Edite" />
        </div>
      );
    }
    
    ReactDOM.render(
      <App />,
      document.getElementById('root')
    );
    

    在CodePen上试一试
    通常,新的React应用在最上层有一个单独的App组件。但是如果你将React集成到现有的应用中,你可以自下而上,从一个小的component开始,比如一个按钮,逐步的整合到层次结构的顶层。

    警告:
    Components必须返回一个单独的根element。这就是为什么我们添加一个<div>来包括所有的<Welcome /> elements。

    提取Components

    大胆的将components分割成更小的components。
    比如,考虑这个Comment component:

    function Comment(props) {
      return (
        <div className="Comment">
          <div className="UserInfo">
            <img className="Avatar"
              src={props.author.avatarUrl}
              alt={props.author.name}
            />
            <div className="UserInfo-name">
              {props.author.name}
            </div>
          </div>
          <div className="Comment-text">
            {props.text}
          </div>
          <div className="Comment-date">
            {formatDate(props.date)}
          </div>
        </div>
      );
    }
    

    在CodePen上试一试
    它接收author(一个object),text(一个字符串)和date(一个date)作为props,描述了社交媒体网站上一则评论。
    由于全是嵌套,这个component很难改变,而且也很难去重用他的各个部分。让我们从中提取几个组件。
    首先,我们来提取Avatar:

    function Avatar(props) {
      return (
        <img className="Avatar"
          src={props.user.avatarUrl}
          alt={props.user.name}
        />
      );
    }
    

    Avatar不需要知道Comment里面究竟在渲染什么东西。因此我们传给prop一个更为通用的名字:user,而不是author
    我们建议从组件自身的观点来命名props,而不是依据组件所在的环境。
    现在我们就稍微简化了下Comment:

    function Comment(props) {
      return (
        <div className="Comment">
          <div className="UserInfo">
            <Avatar user={props.author} />
            <div className="UserInfo-name">
              {props.author.name}
            </div>
          </div>
          <div className="Comment-text">
            {props.text}
          </div>
          <div className="Comment-date">
            {formatDate(props.date)}
          </div>
        </div>
      );
    }
    

    接下来,我们将提取一个UserInfo组件,他渲染一个Avatar和用户的名字:

    function UserInfo(props) {
      return (
        <div className="UserInfo">
          <Avatar user={props.user} />
          <div className="UserInfo-name">
            {props.user.name}
          </div>
        </div>
      );
    }
    

    这使得我们可以进一步简化Comment:

    function Comment(props) {
      return (
        <div className="Comment">
          <UserInfo user={props.author} />
          <div className="Comment-text">
            {props.text}
          </div>
          <div className="Comment-date">
            {formatDate(props.date)}
          </div>
        </div>
      );
    }
    

    在CodePen上试一试
    提取components也许乍看之下像是无用功,但在大型的app中,能有一系列可重用的组件,这么做绝对超值。一个很好的经验法则:如果你的UI中某部分被重复使用多次(按钮,面板,头像),或者自身相当复杂(App,FeedStory,评论), 这都是成为可重用组件的最佳人选。

    Props是只读的

    不论你将component声明为函数还是类,都绝不能去修改它的props。看下这个sum函数:

    function sum(a, b) {
      return a + b;
    }
    

    这样的函数我们称其为"pure",因为他们不回去改变输入,且对于输入一样返回的结果始终都是相同的。
    相比之下,这个函数不是纯函数,因为它改变了自己的输入:

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

    React虽然相当灵活,但它有一个严格的规则:
    所有的React组件必须像个纯函数一样,不得修改他们的props。
    当然,应用的UI是动态的,随着事件而变化。在下一节,我们将引入一个新的概念:"state"。State使得React components可以根据用户的行为,网络响应或者其他什么,使得他们的输出每次不同,而不会违反此规则。

    相关文章

      网友评论

        本文标题:Components和Props

        本文链接:https://www.haomeiwen.com/subject/qkxbzttx.html