React从入门到精通系列之(4)组件化和Props传递

四、组件化和属性(props)

组件允许您将UI拆分为独立的可重用的部分,并单独地考虑每个部分。
从概念上讲,组件就像JavaScript函数。 它们接受任意输入(称为“props”),并返回应该出现在屏幕上的React元素。

功能性组件(functional)和类组件(class component)

定义组件的最简单的方法是编写JavaScript函数:

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

此函数是个有效的React组件,因为它接收一个带有数据的“props”对象作为参数,并返回一个React元素。 我们把这样的组件称为“功能性组件(functional)”,因为它们是个JavaScript函数。

您还可以使用ES6类来定义组件:

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

上述两个组件从React的角度来看是等效的。

类组件有一些额外的功能,我们将在下面的章节中讨论。 在那之前,我们将简单地使用功能组件。

渲染一个组件

以前,我们只遇到使用DOM标签的React元素:

const element = <div />;

但是,元素也可以表示用户自定义的组件:

const element = <Welcome name="zhangyatao" />;

当React看到表示用户定义组件的元素时,它将该JSX标签的所以属性放到一个对象中传递给组件。 我们称这个对象为“props”。

例如,此代码在页面上呈现“Hello,zhangyatao”:

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

const element = <Welcome name="zhangyatao" />;
ReactDOM.render(
    element,document.getElementById('root')
);

让我们回顾一下在这个例子中发生了什么:

  1. 首先调用ReactDOM.render()方法,并处理<Welcome name =“zhangyatao”/>组件。

  2. React使用{name:'zhangyatao'}作为props调用Welcome组件。

  3. 我们的Welcome组件返回一个<h1> Hello,zhangyatao </ h1>元素作为结果。

  4. React DOM有效地根据<h1> Hello,zhangyatao </ h1>来更新DOM。

警告
组件名称始终使用·首字母大写·。
例如,<div />表示一个DOM标签,但<Welcome />表示一个组件,并要求Welcome必须和ReactDOM在一个作用域内。

编写组件

组件可以在其返回值中引用去其他组件。 这允许我们对任何级别的细节使用相同的组件抽象。 一个按钮,一个表单,一个对话框,一个屏幕:在React应用程序中,所有这些通常表示为组件。
例如,我们可以创建一个App组件,让它渲染多个Welcome组件:

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

function App() {
   return (
       <div>
           <Welcome name="zhangyatao" />
           <Welcome name="jiangyanyun" />
           <Welcome name="pangyun" />
       </div>
   );
}

ReactDOM.render(
   <App />,document.getElementById('root')
);

这个新的React应用程序在顶部有一个单独的App组件。
但是,如果您将React集成到现有应用程序中,则可以使用Button这样的小组件自下而上开始逐步向上到达视图层次结构的顶部。

警告
引用多个组件时必须包裹在一个根元素中返回。 这就是为什么我们添加了一个<div>来包含所有<Welcome />元素。

抽离组件

永远不要害怕将组件拆分成更小的组件。
例如,考虑这个Comment组件:

import React from 'react';
import ReactDOM from 'react-dom';

function formatDate(date) {
    return date.toISOString();
}

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>
    );
}

ReactDOM.render(
    <Comment author={{
        avatarUrl: 'https://ss0.bdstatic.com/7Ls0a8Sm1A5BphGlnYG/sys/portrait/item/3ae1dc06.jpg',name: 'zhangyatao'
    }} text={'我的名字叫张亚涛'} date={new Date()}/>,document.getElementById('root')
);

它接受author(作者),text(内容)和date(日期)作为props,用来描述社交媒体网站上的评论。
这个组件可能很难改变,因为所有的嵌套,它也很难重用其中的单个部分。 让我们从中提取几个组件。

首先,我们将提取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 uer={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>
    );
}

最终的代码如下:

import React from 'react';
import ReactDOM from 'react-dom';

function formatDate(date) {
    return date.toISOString();
}
function Avatar(props) {
    return (
        <img className="Avatar"
             src={props.user.avatarUrl}
             alt={props.user.name}
        />
    );
}
function UserInfo(props) {
    return (
        <div className="UserInfo">
            <Avatar user={props.user}/>
            <div className="UserInfo-name">
                {props.user.name}
            </div>
        </div>
    );
}
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>
    );
}
ReactDOM.render(
    <Comment author={{
        avatarUrl: 'https://ss0.bdstatic.com/7Ls0a8Sm1A5BphGlnYG/sys/portrait/item/3ae1dc06.jpg',document.getElementById('root')
);

让一个个可重用组件在大型应用程序中交付使用的过程中,抽离组件起初可能看起来像又脏又累的活儿。 所以有一个好的经验法则:如果UI的一部分被使用了好几次(按钮,面板,头像),或者内部比较复杂的东西(App,FeedStory,评论),一个可重用的组件对它来说可以达到最大的发挥空间。

Props是只读的

无论是将组件声明为功能组件还是类组件,它都不能修改自己的props。 考虑这个计算参数总和的函数:

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

这样的函数被称为“纯函数”,因为它们不会改变它们的参数值,并且对于相同的输入总是返回相同的结果。
相反,这个函数是不纯的,因为它改变自己的参数:

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

React非常灵活,但它有一个严格的规则:
所有React组件必须像它们的porps的纯函数那样运行。

当然,应用中的UI大部分是动态的,随时间变化。 在下一节中,我们将介绍一个“state”的新概念。 状态允许React组件响应用户操作,网络响应和其他任何内容,随时间更改其输出,而不违反此规则。

相关文章

react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接...
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc ...