React入门笔记:一个小表格组件的构建过程

vue,angular,react,这三个框架,本来打算学vue。图书馆资料就一本,就转向了React(图书馆突然买了好多。。。),其实还是因为它能够使用native开发,现在开发安卓我用的是kotlin,想着前端学习,也要加把劲了,迈入更好的路。

一直在印象笔记写:

1:组件入门

TIPS:引入库文件(现在还没到jsx,用了两个,一个核心,一个dom库)

1.1使用预定义组件(这些是预定的组件构造器)

ReactDOM.render();//渲染函数

ReactDOM.render(“what”,where);//绑定用id

React.DOM.*({JSON属性},子节点}//属性省略null, 子节点可以嵌套,组件构造器

ReactDOM.render(

React.DOM.h1(null,"你好,React"),

document.getElementById('example')

);

等同于:

你好


1.2:嵌套使用:(说实话,没用JSX时候,复杂的时候,我都写蒙了,找不到括号匹配了。。。)

ReactDOM.render(

React.DOM.span({style:{color:"red"}},

    React.DOM.em({style:{color:"blue"}}," 我是斜体蓝色span"),"我是正常红色span"),

document.getElementById('ex1')

);

等同于:

<span style="color:"blue">我是斜体蓝色

    <em style="color:"red">我是正常红色</em>

</span>
1.3.使用自定义组件(这个以后是主要使用的啦)

var myELe = React.createClass(

{

propTypes:{

            name:React.PropTypes.string.isRequired,

            title:React.PropTypes.string

                },

getDefaultProps:function(){

            return {

                            title:"我是默认属性值"

                        }

    },

render:function(){

            return React.DOM.span(null,"我是自定义组件span"+this.props.title)//调用属性值

}

});//自定义组件函数

React.createClass();//参数,json格式,render渲染函数,返回React组件

ReactDOM.render(

React.createElement(myEle),

document.getElementById('ex3')

);//引用

工厂模式:

var myEle2 = React.createFactory(myEle);

ReactDOM.render(

myele2(),

document.getElementById('ex3')

);

tips:默认属性值,只能返回非isRequired的属性值,设置接收属性值的对象:propTypes:{属性名:属性值,React.PropTypes.值类型.是否必须

创建组件,渲染传入需要接收的属性值,如果非isRequired没有传,则使用getDefaultProps函数返回的值!

在定义组件对象时,this.props.属性名,可以调用属性

1.4 State状态(对象里面值为函数,称方法)

下面是一个完整的自定义组件与渲染

var myIput = React.createClass({

//默认属性json格式

proptypes:{

    text:React.PropTypes.string

},

//默认属性设置函数

getDefaultProps:function(){

    return {

        text:''

    }

},

//初始化状态函数,动态设置属性值

getInitialState:function(){

    return {

        text:this.props.text

    }

},

//自定义方法,设置state状态

_textChange:function(e){

    this.setState({

        text:e.target.value

    })

},

//渲染函数,设置监听器,onChange:自定义方法

render:function(){

        return React.DOM.div(null,

                   React.DOM.textarea({

                       defaultValue : this.props.text,

                       onChange : this._textChange

               }),

              React.DOM.h3(null,this.state.text.length)

        )

    }

});//自定义组件,一个实时计算输入字符长度的组件

调用:

var myInput_render = ReactDOM.render(

React.createElement(myIput,{text:'default'}),

document.getElementById('ex4'));

1.5 外部访问组件

myInout_render.setState({text:””});//外部访问组件,并改变state,不推荐!

1.6 生命周期方法:使用生命周期给我们后来方面实时修改页面数据

componentWillUpdate()//组件再次渲染调用,如render前调用,组件的props与state发生改变会调用

componentDidUpdate()//render执行完毕,更新的组件同步DOM后调用,不会在初始化调用

componentWillMount()//新节点插入前调用

componentWillDidMount()//新节点插入之后调用

componentWillUnmount()//组件从DOM移除调用

shouldComponentUpdate(newProps,newState)//在componentWillUpdate之前触发

{···

_log:function(Mname,args){

    console.log(Mname,args);

},

componentWillUpdate:function(){

    this._log('componentWillUpdate:渲染前调用|props与state改变调用',arguments)

},

componentDidUpdate:function(){

    this._log('componentWillDidUpdate:render执行完,同步DOM之后调用',arguments)

},

shouldComponentUpdate:function(newProps,newState){

    this._log('shouldComponentUpdate:componentWillUpdate之前触发,返回false能禁止render调用',arguments);

    return true

},

componentDidMount:function(){

    this._log('componentDidMount:新节点插入之后触发',arguments)

},

componentWillMount:function(){

    this._log("componentWillMount:新节点插入之前触发",arguments)

},

componentWillUnmount:function(){

    this._log('componentWillUpdate:组件从DOM移触发',arguments)

}

···}

1.6 mixin 简化重复的生命周期方法(其实就是一个json格式,简化了写法)

var myMixin_one = {

_log:function(Mname,args){

    console.log(Mname,args);

},

componentWillUpdate:function(){

    this._log('componentWillUpdate:渲染前调用|props与state改变调用',arguments)

},

componentDidUpdate:function(){

    this._log('componentWillDidUpdate:render执行完,同步DOM之后调用',arguments)

},

shouldComponentUpdate:function(newProps,newState){

    this._log('shouldComponentUpdate:componentWillUpdate之前触发,返回false能禁止render调用',arguments);

    return true

},

componentDidMount:function(){

    this._log('componentDidMount:新节点插入之后触发',arguments)

},

componentWillMount:function(){

    this._log("componentWillMount:新节点插入之前触发",arguments)

},

componentWillUnmount:function(){

    this._log('componentWillUpdate:组件从DOM移触发',arguments)

}

};

var myMixin_two = {

proptypes:{

    text:React.PropTypes.string

},

getDefaultProps:function(){

    return {

        text:''

    }

},

getInitialState:function(){

    return {

        text:this.props.text

    }

},

_textChange:function(e){

    this.setState({

        text:e.target.value

    })

},

};

var myIput = React.createClass({

render:function(){

        return React.DOM.div(null,

                   React.DOM.textarea({

                       defaultValue:this.state.text,

                       onChange:this._textChange

               }),

              React.DOM.h3(null,this.state.text.length)

        )

    },



mixins:[myMixin_one,myMixin_two]//用mixin替代周期函数与初始化方法

});

1.7 子组件:子组件从属于父组件

//定义父组件

var parCom = React.createClass({

propTypes:{

    text:React.PropTypes.string

},

getDefaultProps:function(){

    return {

        text:''

    }

},



_textChange:function(e){

    this.setState({

        text:e.target.value

    })

},

getInitialState:function(){

    return {

        text:this.props.text

    }

},

render:function(){

    var counter = null;

    if(this.state.text.length>0){

//初始化子组件

        counter = React.DOM.h3(null,React.createElement(sonCom,{

            count:this.state.text.length

        }))

//父组件,渲染时候,检测条件,是否渲染子组件

//如果父组件的text属性值大于0,渲染子组件

    }

    return React.DOM.div(null,

        React.DOM.textarea({

        defaultValue:this.state.text,

        onChange:this._textChange

        }),counter//引用子组件

    )

}

});

//定义子组件

var sonCom = React.createClass({

name:'Counter',

propTypes:{

    count:React.PropTypes.number.isRequired,

},

render:function(){

    return React.DOM.span(null,this.props.count);

}

})

//调用

ReactDOM.render(React.createElement(parCom,{text:’’}),document.getElementById(‘ex5’))
下面是一个完整的表格React应用demo过程部分代码来自:《React快速上手》

1.8 Excel组件

var Excel = React.createClass({

displayName:'Excel',

getInitialState:function(){

    return {

        data:this.props.initialdata

    }

},

render:function(){

    return React.DOM.table({className:"table"+" "+"table-bordered"},//多个类用拼接

            React.DOM.thead(null,

                React.DOM.tr(null,

                    this.props.headers.map(function(title,idx){

//map()方法,将数组的值传入回调函数

// arr.map(function(currentValue,index,arr),thisValue)

//参数说明 function(currentValue,index,arr) 必须,函数,数组中的每个元素都会执行这个函数函数参数 函数参数 currentValue 必须 当前元素值 index 可选 当前元素的索引值 arr 可选 当前元素属于的数组对象。

                        return React.DOM.th({key:idx},title);

                   })

                )

            ),

            React.DOM.tbody(null,this.state.data.map(function(row,idx){

                return React.DOM.tr({key:idx},

                    row.map(function(cell,idx){

                        return React.DOM.td({key:idx},cell)

                    })

                )

            }))

        );

}

});

var headers = [“book”,”author”,”lanage”,”sales”];

var data = [[“math”,”jack”,”en”,”23”],[“english”,”alice”,”23”,”en”]];

ReactDOM.render(React.createElement(Excel,{

headers:headers,

initialdata:data

}),document.getElementById(‘ex6’));

效果图:

实现排序:arr.sort(回调函数)

_sort:function(e){

    var column = e.target.cellIndex;//当前选中的列索引

    var data  = this.state.data.slice();//对数组的复制

    data.sort(function(a,b){

        console.log(a[column],b);

        return a[column]>b[column] ? 1: -1;

    });

    console.log(e.target.cellIndex);

//更新状态

    this.setState({

        data:data

    })

添加监听器:

React.DOM.thead({onClick:this._sort},
实现编辑:这里用了bootstrap的框架,很好看啊

getInitialState:function(){

    return {

        data:this.props.initialdata,

        edit:null

    };

},

_edit:function(e){

    this.setState({

        edit:{

            row:parseInt(e.target.dataset.row,10),

            cell:e.target.cellIndex

        }

    });

},

·····

React.DOM.tbody({

                onDoubleClick:this._edit},this.state.data.map(function(row,rowidx){

                return React.DOM.tr({key:rowidx},

                    row.map(function(cell,idx){

                        var content = cell;

                        var edit = this.state.edit;

                        if(edit && edit.row === rowidx && edit.cell === idx){

                            content = React.DOM.form({

                                onSubmit:this._save

                            },React.DOM.input({

                                type:"text",

                                className:"form-control",

                                defaultValue:content

                            }))

                        }

                        return React.DOM.td({key:idx,"data-row":rowidx},content)

                    },this)

                )

            },this))

效果图:

实现保存:回车保存

_save:function(e){

    e.preventDefault();//阻止默认事件,防止重新加载

    var input = e.target.firstChild;//引用输入框值

    var data = this.state.data.slice();//复制一份数据修改

    data[this.state.edit.row][this.state.edit.cell] = input.value;//修改指定行指定cell数据

    this.setState({

        data:data,//刷新数据

        edit:null//去除input

    })



},

效果图:

完整源代码:有注释可以研究一下!

<div class="panel-heading text-center">

    React Excel (单击列标题排序,双击单元格编辑,回车确认)

</div>

//定义表格组件

var Excel = React.createClass({

displayName:'Excel',//内部为React标记此组件名

getInitialState:function(){

    return {

        data:this.props.initialdata,//初始化数据

        edit:null//是否开启编辑

    };

},

//排序方法

_sort:function(e){

    var column = e.target.cellIndex;//获取列索引

    var data  = this.state.data.slice();//复制数组

//数组排序方法,传入,回调函数(比较两个数据,返回真假)

    data.sort(function(a,b){

        console.log(a[column],b);

        return a[column]>b[column] ? 1: -1;

    });//每次排序后,刷新状态

    console.log(e.target.cellIndex);

    this.setState({

        data:data

    })

},

//编辑方法

_edit:function(e){

    this.setState({

        edit:{

            row:parseInt(e.target.dataset.row,10),//强制转整形,通过自定义的dataset-row

            cell:e.target.cellIndex//获取row与cell即每个单元格的定位

        }

    });

},

//提交保存

_save:function(e){

    e.preventDefault();//阻止浏览器默认事件

    var input = e.target.firstChild;//获取输入框值

    var data = this.state.data.slice();

    data[this.state.edit.row][this.state.edit.cell] = input.value;//修改指定单元格数据

    this.setState({

        data:data,

        edit:null//去除输入框

    })



},

render:function(){

    return React.DOM.table({className:"table"+" "+"table-bordered"},

            React.DOM.thead({

                    onClick:this._sort},//添加单击监听器

                React.DOM.tr(null,

                    this.props.headers.map(function(title,idx){

                        return React.DOM.th({key:idx},title);

                   })

                )

            ),

            React.DOM.tbody({//添加双击监听器

                onDoubleClick:this._edit},this.state.data.map(function(row,rowidx){

                return React.DOM.tr({key:rowidx},

                    row.map(function(cell,idx){

                        var content = cell;

                        var edit = this.state.edit;//获取edit数据,判断覆盖单元格并生成input的定位

                        if(edit && edit.row === rowidx && edit.cell === idx){

                            content = React.DOM.form({

                                onSubmit:this._save//表单事件

                            },React.DOM.input({

                                type:"text",

                                className:"form-control",

                                style:{

                                    maxWidth:"100px"

                                },

                                defaultValue:content

                            }))

                        }

                        return React.DOM.td({key:idx,"data-row":rowidx},content)

                    },this)

                )

            },this))

        );

}

});

var headers = [“图书”,”作者”,”语言”,”售价”];

var data = [[“数学书”,”李”,”中文”,”23元”],[“英语书”,”爱丽丝”,”英文”,”26元”],[“PHP编程”,”鲍勃”,”英文”,”21元”],[“React笔记”,”bore”,”中文”,”26元”]];

ReactDOM.render(React.createElement(Excel,{

headers:headers,

initialdata:data

}),document.getElementById(‘ex6’));
现在用JSX简化表格组件

1.9 JSX实现Excel组件:JSX在学习时候,引入browser.js来转译jsx到js,实际使用要先转译再使用!


<html>

<head>

<meta charset="UTF-8" />

<title>React笔记</title>

<script src="react.js"></script>

<script src="react-dom.js"></script>

<script src="browser.min.js"></script>JSX转换插件

<script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.js"></script>

<script src="https://cdn.bootcss.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>

<link href="https://cdn.bootcss.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">

<style type="text/css">



.border{

    border: 1px solid red;margin: 10px;padding: 10px;

}

.borderIn{

    border: 1px solid blue;margin: 10px;padding: 10px;

}



</style>

</head>

<body>

<div class="panel panel-primary">

    <div class="panel-heading text-center">

        React Excel (单击列标题排序,双击单元格编辑,回车确认)

    </div>

<div id="ex6" class="panel-body"></div>

</div>



<script type="text/babel">



var Excel = React.createClass(

{    

    /*定义内部组件名字*/

    displayName:'Excel',

    /*定义接收属性*/

    propTypes:{

        headers:React.PropTypes.arrayOf(React.PropTypes.string),/*初始化头部,接收数组,数组值为string*/

        initialdata:React.PropTypes.arrayOf(React.PropTypes.arrayOf(React.PropTypes.string))

        },/*初始化内容,数据接收数组,值为string*/

        /*初始化属性*/

    getInitialState:function(){

        return {

            data:this.props.initialdata,/*初始化组件内部属性属性值*/

            sortby:null,/*排序*/

            edit:null,/*编辑*/

            search:false,/*搜索*/

            desc:false

        };

    },

    /*排序方法*/

    _sort:function(e) {

        var cloumn = e.target.cellIndex;/*获取触发所在的列索引*/

        console.log(e.target.cellIndex);

        var data = this.state.data.slice();/*复制数据操作*/

        var desc = this.state.desc;

        data.sort(function(a,b) {

            console.log(a);

            console.log(b);

            return desc ? (a[cloumn]>b[cloumn] ? 1 : -1) : (a[cloumn]<b[cloumn] ? 1 : -1);/*判断排序*/

        });

        /*更新状态*/

        if(desc == false){

            desc = true;

        }else{

            desc = false;

        }

        this.setState({

            data:data,

            desc:desc

        });

    },

    /*编辑方法*/

    _edit:function(e){

        this.setState({

            edit:{

                row:parseInt(e.target.dataset.row,10),

                cell:e.target.cellIndex,

            }

        });/*刷新状态:定位点击单元格位置*/

    },

    /*保存方法*/

    _save:function(e){

        e.preventDefault();/*阻止默认网页重载*/

        var input = e.target.firstChild;/*获取单元格文本*/

        var data = this.state.data.slice();/*复制数据*/

        data[this.state.edit.row][this.state.edit.cell] = input.value;/*将单元格文本存储到指定data位置*/

        this.setState({

            data:data,

            edit:null,});

    },

    _json:function(){



    },

    _csv:function(){



    },

    _search:function(){



    },

    /*渲染工具栏*/    

    render_head:function(){

        return (

            <div className="container-fluid">

                <div className="row">

                    <div className="col-xs-2" >

                        <button type="button" onClick={this._search}  style={{margin:"5px",width:"100%"}} className="btn btn-primary" >搜索</button>

                    </div>

                    <div className="col-xs-2">

                        <button type="button" onClick={this._json} style={{margin:"5px",width:"100%"}} className="btn btn-default">导出JSON</button>

                    </div>

                    <div className="col-xs-2">

                        <button type="button"onClick={this._csv} style={{margin:"5px",width:"100%"}} className="btn btn-success">导出CSV</button>

                    </div>

                </div>

            </div>

            )



    },

    /*渲染表格*/

    render:function(){

        return     (/*这个括号不可以换行。。。*/

            <div>/*不乐意返回多个顶级节点,用div包裹*/

                {this.render_head()}

                <table className="table table-bordered">

                <thead onClick={this._sort}>

                    <tr>{

                        /*在{}中编写JS*/

                        this.props.headers.map(function(title,idx){

                            return <th key={idx}>{title+=this.state.desc ? '\u2193' : '\u2191'}</th>;



                        },this)

                        }

                    </tr>

                </thead>

                <tbody onDoubleClick={this._edit}>

                    {

                        this.state.data.map(function(row,rowidx){

                            return (

                                <tr key={rowidx}>

                                    {

                                        row.map(function(cell,idx){

                                            var content = cell;

                                            var edit = this.state.edit;

                                            if(edit && edit.row === rowidx && edit.cell === idx){

                                                content = (

                                                    <form onSubmit={this._save}>

                                                        <input style={{maxWidth:"100px"}} type="text" className="form-control" defaultValue={cell} />

                                                    </form>);

                                            }

                                            return <td key={idx} data-row={rowidx}>{content}</td>

                                        },this)

                                    }

                                </tr>

                            );

                        },this)

                    }

                </tbody>

            </table>

            </div>

            );

        

    },



    }

);



var headers = ["图书","作者","语言","售价"];

var data = [["数学书","李","中文","23元"],

["英语书","爱丽丝","英文","26元"],

["PHP编程","鲍勃","英文","21元"],

["React笔记","bore","中文","26元"]];



ReactDOM.render(React.createElement(Excel,{



    headers:headers,

    initialdata:data

}),document.getElementById('ex6'));





</script>

</body>

</html>```


暂时就这样啦。