代码人生的小狗窝

一行行枯燥的代码,却描绘出人生的点点滴滴

您现在的位置是:首页>_JavaScript

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

发布时间:2018-05-22浏览(2413)

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