ReactJS学习笔记(二)-组件嵌套与组件复用

我们终要远行,最终告别稚嫩的自己。

使用React来构建web应用,每个页面都将是多个组件组成,并且相互嵌套来构成的,接下来就学习下组件的嵌套。

一、组件嵌套:

背景交代:

1、创建一个html,包含引用的相关js、需要被渲染的div;

2、创建一个有label与input标签组成的简单组件 — IvanInput,并可以通过传入数组来渲染多组label与input标签,拥有不同的label名称、inputType、inputRef、inputName及input的onChange事件;

3、创建一个button与a标签组成的简单组件 — IvanButton,并可以通过传入的数组来渲染一组button与啊标签,拥有不同的buttonName、button的onClick事件、a标签名称、a标签的onClick事件;

4、创建一个IvanPage组件,包含一个h1标签、多个IvanInput组件、一个IvanButton组件。

1、创建一个html,名称随便起哈:

<!DOCTYPE html>
<html>
<head lang="en">
    <!--设置为utf-8防止中文乱码-->
    <meta charset="UTF-8">
    <title></title>
    <script src="../../dist/react/react.js"></script>
    <script src="../../dist/react/JSXTransformer.js"></script>
    <script src="../../dist/jquery/jquery.min.js"></script>
    <!--如下的这种引用方式是不正确的,必须使用上面的引用方式-->
    <!--<script src="../../dist/react/JSXTransformer.js"/>-->
    <!--自定义的js文件路径-->
    <script type="text/jsx" src="../../js/react/ivanPage.js"></script>
    <!--简单的样式可以忽略不计-->
    <style>
        body{TEXT-ALIGN: center;}
        #index-0329-0041{ MARGIN-RIGHT: auto;
            MARGIN-LEFT: auto;
            height:200px;
            width:400px;
            vertical-align:middle;
        }
    </style>
</head>
<body>
<!--需要被渲染的div,id与自定义的css样式一致即可-->
<div id="index-0329-0041"></div>
<script type="text/jsx">
    /*你的js代码块*/
</script>
</body>
</html>

2、创建一个js文件(步骤1中引用的IvanPage.js),并在js中创建IvanInput组件:

var IvanInput = React.createClass({
    //渲染DOM,相当于java的main函数
    render: function () {
        return (
            <div>
                <label>用户名:</label>
                <input type=‘text‘ ref=‘userName‘ name=userName onChange=this.textOnChange()/>
            </div>
        );
    }
});

为了使组件可以复用,并符合背景交代中的第二条,我需要将label名称、input标签的type、ref、name及onChange事件使用props参数的形式传递给IvanInput组件,更新上方代码为:

var IvanInput = React.createClass({
    //渲染DOM,相当于java的main函数
    render: function () {
        return (
            <div>
                <label>{this.props.labelName}:</label>
                <input type={this.props.inputType} ref={this.props.inputRef} name={this.props.inputName} onChange={this.props.onChangeMethod}/>
            </div>
        );
    }
});

如果想使用IvanInput组件,你就必须给我传递过来以下参数:labelName、inputType、inputRef、inputName、onChangeMethod(这是一个方法),即:

React.render(
        <IvanInput labelName = ‘用户名‘ inputType=‘text‘ inputRef=‘userName‘ inputName=‘userName‘ onChangeMethod={方法}/>,
        document.getElementById(‘被渲染的id‘)
    );

3、同理,继续在此js文件中创建另一个组件IvanButton:

var IvanButton = React.createClass({
    render: function () {
        return (
            <div>
                <button onClick={this.props.buttonClickMethod} >{this.props.buttonName}</button>
                <a onClick={this.props.aHrefClick}>{this.props.aName}</a>
            </div>
        );
    }
});

如果要使用IvanButton组件,需要传递以下参数:buttonClickMethod(这是一个方法)、buttonName(button名称)、aHref(这是一个方法)、aName(a标签名称),即:

React.render(
        <IvanButton buttonName=‘登录‘ buttonClickMethod={button的点击事件}
                            aName=‘忘记密码?‘ aHrefClick={a标签的点击事件}/>,
        document.getElementById(‘被渲染的id‘)
    );

4、继续在js中创建IvanPage组件,并对IvanInput及IvanButton两个组件进行调用:

根据2、3步骤中的调用方式,需要建立3个方法(onSubmit:button的点击事件、textOnChange:文本框方式变化事件、aHrefClick:a标签的onClick事件),同时希望h1标签的值是通过参数的方式传递过来的。

var IvanPage = React.createClass({
    //button的点击事件
    onSubmit:function(){
        console.log(‘onSubmit被点击了‘);
    },

    //文本框的点击事件
    textOnChange: function (e) {
        console.log(‘文本信息发生变化‘);
        //TODO 处理相关验证任务
    },

    //a标签的点击事件
    aHrefClick: function () {
        console.log(‘a标签被点击‘);
    },

    render: function () {
        return(
            <div>

                <h1>{this.props.titleName}</h1>
                <IvanInput labelName = ‘用户名‘ inputType=‘text‘ inputRef=‘userName‘ inputName=‘userName‘ onChangeMethod={this.textOnChange}/>
                <IvanButton buttonName=‘提交‘ buttonClickMethod={this.onSubmit}
                            aName=‘忘记密码?‘ aHrefClick={this.aHrefClick}/>
            </div>

        );
    }
});

调用IvanPage,并传递参数titleName,更新步骤1中的html代码如下:

<script type="text/jsx">
    /*你的js代码块*/
    React.render(
        <IvanPage titleName="登录页面" />,
        document.getElementById(‘index-0329-0041‘)
    );
</script>

运行下你的html文件吧,并点击“提交”、“忘记密码?”、文本框输入相关数值查看控制台输出吧,效果如下:

5、登录!登录!登录!特么就一个用户名搞啥子哟?对的,需要一个“密码”!由于密码与用户名的本质区别就是label与input不同,而我们的IvanInput组件只有label与input两个标签,并可以通过传递不同的参数来展示不同的DOM,那么问题就简单了,拷贝一份IvanInput的调用即可。

更新IvanPage组件,重复调用IvanInput组件(参数不同),还需要为密码框创建一个单独的点击事件pwdOnChange:

var IvanPage = React.createClass({
    //button的点击事件
    onSubmit:function(){
        console.log(‘onSubmit被点击了‘);
    },

    //文本框的点击事件
    textOnChange: function (e) {
        console.log(‘文本信息发生变化‘);
        //TODO 处理相关验证任务
    },

    //密码框的点击事件
    pwdOnChange: function (e) {
        console.log(‘密码信息发生变化‘);
        //TODO 处理相关验证任务
    },

    //a标签的点击事件
    aHrefClick: function () {
        console.log(‘a标签被点击‘);
    },

    render: function () {
        return(
            <div>
                <h1>{this.props.titleName}</h1>
                <IvanInput labelName = ‘用户名‘ inputType=‘text‘ inputRef=‘userName‘ inputName=‘userName‘ onChangeMethod={this.textOnChange}/>
                <IvanInput labelName = ‘密码‘ inputType=‘password‘ inputRef=‘userPwd‘ inputName=‘userPwd‘ onChangeMethod={this.pwdOnChange}/>
                <IvanButton buttonName=‘提交‘ buttonClickMethod={this.onSubmit}
                            aName=‘忘记密码?‘ aHrefClick={this.aHrefClick}/>
            </div>

        );
    }
});

运行下步骤1中的html文件,是不是多了个密码框?这才是登录页面嘛!

问题:

虽然实现了组件的嵌套调用,但这样的IvanPage组件样式及内容已经完全固定,无法更改,这就没办法复用组件,改如何解决呢?

6、要解决上面提出的问题,就需要在最外层传递参数,也就是html里进行参数的传递,通过不同的参数来影响IvanPage页面的内容及样式信息。

  • 定义inputdata/otherdata数组,存放渲染IvanInput/IvanButton组件的必要参数信息,将inputdata与otherdata参数传递给IvanPage组件,更新html中的代码:
<script type="text/jsx">
    /*你的js代码块*/
    var inputdata = [
        {enable:false,labelName:‘用户名‘,inputType:‘text‘,inputRef:‘userName‘,inputName:‘userName‘,method:0},
        {enable:false,labelName:‘密码‘,inputType:‘password‘,inputRef:‘userPwd‘,inputName:‘userPwd‘,method:1}
    ];

    var otherdata = {buttonName:‘登录‘,titleName:‘登录界面‘,aName:‘忘记密码?‘};

    React.render(
            <IvanPage inputdata={inputdata} otherdata={otherdata}/>,
            document.getElementById(‘index-0329-0041‘)
    );
</script>
  • 接收传递的props数据,并存入state中,更新js中的IvanPage组件,加入初始化方法getInitialState,进行数据的接收,并分别命名为inputdata及otherdata:
getInitialState: function () {
        return {inputdata:this.props.inputdata,otherdata:this.props.otherdata}
    }
  • 遍历inputdata数组,进行批量渲染IvanInput组件,获取otherdata数组中的数据,并传递给IvanButton组件。
render: function () {
        {
            var onChangeMethod = [this.textOnChange,this.pwdOnChange];
            //优先生成多个Input组件
            var IvanInputs =  this.state.inputdata.map(function(d){
                return(
                    //查看官方文档,使用map时需定义一个key变量
                    <IvanInput key={d.inputType} labelName = {d.labelName} inputType={d.inputType} inputRef={d.inputRef}
                               inputName={d.inputName} onChangeMethod={onChangeMethod[d.method]}/>
                );
            });
        }
        return(
            <div>
                <h1>{this.state.otherdata.titleName}</h1>
                {IvanInputs}
                <IvanButton buttonName={this.state.otherdata.buttonName} buttonClickMethod={this.onSubmit}
                            aName={this.state.otherdata.aName} aHrefClick={this.aHrefClick}/>
            </div>

        );
    }

完整代码请见最后。。。

至此,组件的嵌套就搞完了,其实也搞定了组件的复用,怎么复用这些组件呢?


二、组件的复用

可以使用如下的两种方式进行组件的复用:

1、最简单的办法,就是在拷贝上面的html文件,更改html文件中的数组信息,即可实现组件的复用了,例如:

<!DOCTYPE html>
<html>
<head lang="en">
    <!--设置为utf-8防止中文乱码-->
    <meta charset="UTF-8">
    <title></title>
    <script src="../../dist/react/react.js"></script>
    <script src="../../dist/react/JSXTransformer.js"></script>
    <script src="../../dist/jquery/jquery.min.js"></script>
    <!--如下的这种引用方式是不正确的,必须使用上面的引用方式-->
    <!--<script src="../../dist/react/JSXTransformer.js"/>-->
    <!--自定义的js文件路径-->
    <script type="text/jsx" src="../../js/react/IvanPage-csdn.js"></script>
    <!--简单的样式可以忽略不计-->
    <style>
        body{TEXT-ALIGN: center;}
        #index-0329-0042{ MARGIN-RIGHT: auto;
            MARGIN-LEFT: auto;
            height:200px;
            width:400px;
            vertical-align:middle;
        }
    </style>
</head>
<body>
<!--需要被渲染的div,id与自定义的css样式一致即可-->
<div id="index-0329-0042"></div>
<script type="text/jsx">
    /*你的js代码块*/
    var inputdata = [
        {enable:false,labelName:‘邮箱‘,inputType:‘email‘,inputRef:‘userEmail‘,inputName:‘userEmail‘,method:0},
        {enable:false,labelName:‘验证码‘,inputType:‘text‘,inputRef:‘authCode‘,inputName:‘authCode‘,method:1}
    ];

    var otherdata = {buttonName:‘找回密码‘,titleName:‘找回密码‘,aName:‘发送验证码‘};

    React.render(
            <IvanPage inputdata={inputdata} otherdata={otherdata}/>,
            document.getElementById(‘index-0329-0042‘)
    );
</script>
</body>
</html>

运行效果如下:

2、通过相关事件来更改state中的参数值,ReactJS会自动的重新渲染整个页面完成组件的复用,那么就在a标签的点击事件中来简单的尝试一下吧!

更新IvanPage组件中的aHrefClick方法:

 aHrefClick: function () {
        console.log(‘a标签被点击‘);
        if(this.state.otherdata.aName == ‘忘记密码?‘){
            var inputdata = [
                {enable:false,labelName:‘邮箱‘,inputType:‘email‘,inputRef:‘userEmail‘,inputName:‘userEmail‘,method:0},
                {enable:false,labelName:‘验证码‘,inputType:‘text‘,inputRef:‘authCode‘,inputName:‘authCode‘,method:1}
            ];

            var otherdata = {buttonName:‘验证‘,titleName:‘找回密码‘,aName:‘发送验证码‘};

            this.setState({inputdata:inputdata,otherdata:otherdata});
        }else{
            //TODO something
        }
    },

这时候点击登录页面的“忘记密码?”时,页面将被ReactJS重新渲染成一个新的DOM,就完成了组件的复用功能,是不是很屌的样子,其实人家ReactJS本来就很屌的。

3、完整代码:

  • html
<!DOCTYPE html>
<html>
<head lang="en">
    <!--设置为utf-8防止中文乱码-->
    <meta charset="UTF-8">
    <title></title>
    <script src="../../dist/react/react.js"></script>
    <script src="../../dist/react/JSXTransformer.js"></script>
    <script src="../../dist/jquery/jquery.min.js"></script>
    <!--如下的这种引用方式是不正确的,必须使用上面的引用方式-->
    <!--<script src="../../dist/react/JSXTransformer.js"/>-->
    <!--自定义的js文件路径-->
    <script type="text/jsx" src="../../js/react/ivanPage.js"></script>
    <!--简单的样式可以忽略不计-->
    <style>
        body{TEXT-ALIGN: center;}
        #index-0329-0041{ MARGIN-RIGHT: auto;
            MARGIN-LEFT: auto;
            height:200px;
            width:400px;
            vertical-align:middle;
        }
    </style>
</head>
<body>
<!--需要被渲染的div,id与自定义的css样式一致即可-->
<div id="index-0329-0041"></div>
<script type="text/jsx">
    /*你的js代码块*/
    var inputdata = [
        {enable:false,labelName:‘用户名‘,inputType:‘text‘,inputRef:‘userName‘,inputName:‘userName‘,method:0},
        {enable:false,labelName:‘密码‘,inputType:‘password‘,inputRef:‘userPwd‘,inputName:‘userPwd‘,method:1}
    ];

    var otherdata = {buttonName:‘登录‘,titleName:‘登录界面‘,aName:‘忘记密码?‘};

    React.render(
            <IvanPage inputdata={inputdata} otherdata={otherdata}/>,
            document.getElementById(‘index-0329-0041‘)
    );
</script>
</body>
</html>
  • ivanPage.js
/**
 * Created by ivan on 2016/3/31.
 */

/*
 定义组件(首字母比较大写),相当于java中的类的声明
 */
var IvanInput = React.createClass({
    //渲染DOM,相当于java的main函数
    render: function () {
        return (
            <div>
                <label>{this.props.labelName}:</label>
                <input type={this.props.inputType} ref={this.props.inputRef} name={this.props.inputName} onChange={this.props.onChangeMethod}/>
            </div>
        );
    }
});

var IvanButton = React.createClass({
    render: function () {
        return (
            <div>
                <button onClick={this.props.buttonClickMethod} >{this.props.buttonName}</button>
                <a onClick={this.props.aHrefClick}>{this.props.aName}</a>
            </div>
        );
    }
});

var IvanPage = React.createClass({
    //button的点击事件
    onSubmit:function(){
        console.log(‘onSubmit被点击了‘);
    },

    //文本框的点击事件
    textOnChange: function (e) {
        console.log(‘文本信息发生变化‘);
        //TODO 处理相关验证任务
    },

    //密码框的点击事件
    pwdOnChange: function (e) {
        console.log(‘密码信息发生变化‘);
        //TODO 处理相关验证任务
    },

    //a标签的点击事件
    aHrefClick: function () {
        console.log(‘a标签被点击‘);
        if(this.state.otherdata.aName == ‘忘记密码?‘){
            var inputdata = [
                {enable:false,labelName:‘邮箱‘,inputType:‘email‘,inputRef:‘userEmail‘,inputName:‘userEmail‘,method:0},
                {enable:false,labelName:‘验证码‘,inputType:‘text‘,inputRef:‘authCode‘,inputName:‘authCode‘,method:1}
            ];

            var otherdata = {buttonName:‘验证‘,titleName:‘找回密码‘,aName:‘发送验证码‘};

            this.setState({inputdata:inputdata,otherdata:otherdata});
        }
    },

    getInitialState: function () {
        return {inputdata:this.props.inputdata,otherdata:this.props.otherdata,firstParam:‘‘,secondParam:‘‘}
    },

    render: function () {
        {
            var onChangeMethod = [this.textOnChange,this.pwdOnChange];
            //优先生成多个Input组件
            var IvanInputs =  this.state.inputdata.map(function(d){
                return(
                    //查看官方文档,使用map时需定义一个key变量
                    <IvanInput key={d.inputType} labelName = {d.labelName} inputType={d.inputType} inputRef={d.inputRef}
                               inputName={d.inputName} onChangeMethod={onChangeMethod[d.method]}/>
                );
            });
        }
        return(
            <div>
                <h1>{this.state.otherdata.titleName}</h1>
                {IvanInputs}
                <IvanButton buttonName={this.state.otherdata.buttonName} buttonClickMethod={this.onSubmit}
                            aName={this.state.otherdata.aName} aHrefClick={this.aHrefClick}/>
            </div>

        );
    }
});

时间: 2024-11-07 13:33:21

ReactJS学习笔记(二)-组件嵌套与组件复用的相关文章

Android学习笔记二十五.Service组件入门(三)使用IntentService

使用IntentService 1.Service缺陷 由于Service本身存在以下两个问题: (1)Service不会专门启动一条单独的进程,Service与他所在应用位于同一进程中: (2)Service也不是专门一条新的线程,如果我们在Service中直接处理耗时的任务,那么就会导致应用程序出现假性"卡死".如果我们需要在Service处理耗时任务,也可以在Service的onCreate()方法中启动一条新线程来处理该耗时任务(如上例).但是,问题来了,启动Service的A

Citrix XenMobile学习笔记之五:XenMoble架构组件

XenMobile Enterprise包含了电子邮件和网页浏览等安全的移动生产应用程序,并且保障档共享.同步处理和编辑的安全.Citrix凭借这个无可比拟的整合式移动服务,成功在企业移动办公市场上脱颖而出,傲视同侪. 现今的工作模式讲求从各种移动装置上安全和顺利地存取应用程序与资料.员工则需从易用的统一化企业app store,获取移动.Windows.网络及软件即服务(Software as a service,SaaS)应用程序.员工需要能够随时随地检视.编辑.同步处理和分享资料,并在各种

Spring Batch学习笔记二

此系列博客皆为学习Spring Batch时的一些笔记: Spring Batch的架构 一个Batch Job是指一系列有序的Step的集合,它们作为预定义流程的一部分而被执行: Step代表一个自定义的工作单元,它是Job的主要构件块:每一个Step由三部分组成:ItemReader.ItemProcessor.ItemWriter:这三个部分将执行在每一条被处理的记录上,ItemReader读取每一条记录,然后传递给ItemProcessor处理,最后交给ItemWriter做持久化:It

Android学习笔记二十九之SwipeRefreshLayout、RecyclerView和CardView

Android学习笔记二十九之SwipeRefreshLayout.RecyclerView和CardView 前面我们介绍了AlertDialog和几个常用的Dialog,ProgressDialog进度条提示框.DatePickerDialog日期选择对话框和TimePickerDialog时间选择对话框.这一节我们介绍几个新的API控件SwipeRefreshLayout.RecyclerView和CardView,这几个API控件都是google在Android5.0推出的.下面我们来学

【Unity 3D】学习笔记二十六:unity游戏脚本(六)

在3D游戏世界中,任何一个游戏对象在创建的时候都会附带Transform(变换)组件,并且该组件是无法删除的,也不应该删除.在unity中,Transform面板一共有3个属性: Position  (位置) Rotation(旋转) Scale(缩放) 这三个值都是用来调整游戏对象在游戏界面中的位置,状态等相关参数. Position  (位置) 任何一个游戏对象的三维坐标都保存在Vector3容器中,该容器记录对象在X轴,Y轴,Z轴的坐标.一旦Vector33容器中的坐标发生变化,那么Sce

Android学习笔记二

17. 在ContentProvider中定义的getType()方法是定义URI的内容类型. 18. SQLiteDatabase类中的insert/delete/update/query方法其实也挺好用的,我在EquipmentProvider类中做了实现 19. Android专门有个单元测试项目(Android Test Project),在这个项目中,可以新建一个继承AndroidTestCase类的具体测试类来单元测试某个功能.我新建了一个AndroidTestProject项目,在

CCNA 学习笔记(二)--CISCO设备初接触

CCNA 学习笔记(二)--初识CISCO设备 上一章我们认识到网络方面的基础知识了,现在我们正式接触CISCO设备. 1.首先我们先了解一台CISCO设备里的主要组件: ROM(只读存储器):主要保存着开机自检软件.保存路由器启动引导程序. RAM(随机存储器):断电会把数据丢失.可以存储配置文件.>>>相当于PC的内存 NVRAM(非易失存储器):断电不会丢失.用来保存用户配置文件.IP.startup-config.主机名.路由协议等等. FLASH(闪存):主要是保存完整的   

Android学习笔记二十四之ListView列表视图二

Android学习笔记二十四之ListView列表视图二 前面一篇我们介绍了常用的几种适配器的简单实现和ListView的简单使用,这一篇中,我们介绍一下ListView的优化和一些其它的问题. ListView优化方法一 在ListView中,我们最常用的就是自定义Adapter,在我们自定义Adapter中,需要实现两个比较重要的方法getCount()和getView(),前者是负责计算ListView的总Item数,后者是生成Item,有多少个Item就会调用getView()方法多少次

angularjs 学习笔记(二) ----- bootstrap框架

1.  下载新的jquery-1.11.1文件. 2.  下载新的bootstrap文件. 3.  选择流式布局的模板填充入index.html文件. 4.  将top.foot转为nginclude文件 nginclude必须使用$scope对象,因此需要设置一个全局的mainctrl来将字符串或全局变量注入$cope中. $rootScope.global =global; 直接将全局变量global赋值给$rootScope,并且之后的子$Scope会获得继承,静态定义与ctrl分开. 5