开发者学堂课程【高校精品课-上海交通大学 -互联网应用开发技术:React samples 1】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/76/detail/15746
React samples 1
我们来看一下他给的这个类似的代码,就是它定义了一个类,
class test extends Component {
constructor(props){
super(props);
this.statei= {
liked:false
};
}
handleClick(event){
this.setstate({liked:!this.state.liked});
}
render() {
var text = this.state.liked ?'喜欢’:'不喜欢';
return(
<div onClick={this.handleClick}>
你<b>{text}</b>我。点我切换状态。
</div>
);
}
export default test;
它在构造器里面,有一个state,是like:false,这个state的指向非常明确,就是指的当前要创建的text的实例,即Component的一个实例。
单纯的讲这个对象,它的state里面有一个叫like的false,下面有它定义的成员函数,就是handle click,意思是如果有人点击handle click,会传递一个实践进入,即把当前这个对象属于text实例,因为handleclick是这个类的一个成员,属于这个实例,就在这个实例上去调setstate,相当于把这个like的属性和状态给反一下,就原来是false,给它改成true,否则的话改成反过来,切换一下,底下是render就像画这个test,要把它渲染出来,前面不需要进行操作,下端可以看到,它会是分离的div,div上就会有一个点击的动作,在这一片空间内那个div所在这空间的一侧点击。
调this handle click函数,一旦在div这个地方click一下,去去handle click函数,这个函数就会把当前的状态改写掉,这个程序就是类似书上的例子,如果直接将它改写成一个类的话,去执行会出现这类似的问题,查一下这个问题,他就会报这个错误,即TypeError:Cannot read property‘setState’of underfine这个setState,没有定义过,虽然听起来很奇怪,但是意思就是在这里面setState没有定义过,代码中的this就是当前这个test的setState,这里面有一个问题,当把这个函数放到这里的时候,javascript里面的这个this的作用就是,如果你写的这个是普通函数,它这个意思是动态绑定,看一下这个过程,它是在div里面调了,这个this handle click这里没问题,这个this是动态绑定,它肯定绑不是这个当前的steState,它极有可能绑的就是这个div,所以他会爆出这个div上是没有steState的。处理这个方法的方式有两个方法,第一个就是我们上节课看到举的例子里面也有这类似的一句话,
第一种解决方法是:手动绑定this。将
constructor(props) {
super(props);
this.state = {
liked:false
};
}
改为
constructor(props){
super(props);
this.state = {
liked: false
};
this.handleClick = this.handleClick.bind(this);//手动绑定
}
在构造器里面,这个this一定指的是当前这个对象,在这里面把他绑死,就是handleclick这个方法里面的this,就是绑着当前的这个对象。
所以一种方法是你用这种方式,但这么做比较麻烦。一个最简单的方法就是用箭头函数。箭头函数实际上我们看他像个箭头,本质上来说,他就是个拉姆达表达式,其他的语言里面也有这个特性。这个简便写法相当于定义了一个匿名的函数,函数没有名字,他有个参数列表,这就源括号里的参数列表,这像个关键字,后面是他要执行的东西,它执行的就是你要写的,就像上面写的函数题一样,把你要的执行代码写进去,那这时候这个我们看到的这个。
在这上面它就调用这个setState,现在我们来看这个this,他就不是动态绑定了。因为箭头函数的这个这个字,它是静态绑定的,也就是说这个博客底下还讲了很多其他的话,但最重要的是在最后他讲了这样一句话。就是这个拉姆达表达式,他不是动态绑定这个作用力。他不是根据这个程序运行的位置来确定它的值是什么,它是一个静态的,把这个方式做出来的,它会根据你当时在进行定义的时候的那个位置确定。换句话说,其实在写代码的时候,你在写到这个地方的时候,它在加载的时候就已经给他绑定了。就像我们在构建类似于构造器一样的,在创建对象的时候直接绑死他也是一样,当时在声明的时候,我就已经绑死他,跟当前这个对象绑定就不会在。
这里面这个类似游戏动态去绑定,它属于哪个对象。这就是用箭头函数来进行处理的一个原因,关于箭头函数的使用本身。在这边,英文的网站是这个,JavaScript: Arrow Functions for Beginners他写的比较全面,那针对他有中文的一个翻译,上节课的内容中只是给大家讲了一下this指代如果不用箭头函数会出错,现在看这边的解释就知道,如果不用建造函数的类似的绑定是不确定的,就不是动态的去绑定它,取决于你在调用他的那一刻,上面写的这个东西。
其实调用这个handleclick的时候,当时他的商家跟环境怎么样。那在这里,它指向的应该就是这个div,所以才会报这个错,把它变成这种函数,它在声明的时候尽快去绑定,就不会出现这个错了。那今天这个PPT要介绍的就是类似于图书的一个展示的东西,如下图:
Book |
Author |
Language |
Published |
Sales |
Dream of the Red Chamber |
Cao Xueqin |
Chinese |
1754-1791 |
100 million |
She: A History of Adventure |
H.Rider Haggard |
English |
1887 |
100 million |
The Hobbit |
J.R. R. Tolkien |
English |
1937 |
100 million |
And Then There Were None |
Agatha Christie |
English |
1939 |
100 million |
Le Petit Prince(The Little Prince) |
Antoine de Saint-Exupéry |
French |
1943 |
140 million |
The Lord of the Rings |
J.R.R.Tolkien |
English |
1954-1955 |
150 million |
Harry Potter and the Philosopher's Stone |
J. K. Rowling |
English |
1997 |
107 million |
那是通过创建一个静态的一个react的APP来开发这个系统的。在开发的时候会碰上,创建了一个系统,创建这个工程之后,需要等很长时间。下载的比较慢,因为他在下载这边,我们所看到的node modules里面的东西,需要依赖到的所有东西,他在下载,,你的工程里的东西,你可以看到全部是自己的要用的东西,包括一个静态页面,包括用到的一些图,还有自己写的,其他东西都没有,全部都需要去通过下载得到,现在速度慢也没有解决办法。在这个root底下画一个标签,剩下的事情就是我们自己来开发,自己可以组织自己的代码,因为这个例子比较简单,没有再去把它划分,用这个例子大家可以看到,其实这里面有两个js。
一个是系统,在创建这个工程组成默认的代理,还有一个是为了出现这样的效果。这个按钮的效果,我用了这个书上的例子写的这个js,两个js都用的,所以我们在作业里面就是说,如果多个js,你该怎么去处理的处理方法。如果想实现上表中的功能,要先定义两个常量,一个是表头
Book |
Author |
Language |
Published |
Sales |
起常量名叫header,在二维数组中写死,放入对应表格五列中的值,
后面的例子就会改写这些例子,会从后台抓取,现在的页面实际上是从后面的后台中动态抓取的,如果不用现在的页面或者构架,需要每次自己生成页面,是不可取的。
class Excel extends React.Component {
constructor(props){
super(props);
this.state ={
data: this.props.initialData,
soctby: null,
descending: false,
edit: null, // [row index, cell index],
search: false,
preSearchData: null,
};
}
这个例子里面有一个构造器,这个构造器中有外面传递进来的属性,在绘制时传递的属性即下段程序中的header和data,这就是上述代码在创建excel时所需要的参数,会把所有参数放入props,把initialdata赋给data,data就是将来在表格页面呈现的数据,可以在表格页面search一旦做search就会过滤页面中的数据,data是会变化的,所以它是个state,而不是props,副节点传递进来的不能直接去改变数值,只能是data赋值给state去修改。
function App() {
return (
ReactDOM.render(
React.createElement(Excel, props:{
headers: headers,
initialData: data
}),
document.getElementById( elementld: "root")
));}
表格的排序,在某一列这个属性也在不断变化,升序和降序也会因为用户的点击发生变化,也要被设为state,然后底下edit这个是什么意思呢?
它是我们在任何一个位置上,如果双击它,你就会看到它可以变成进入可编辑状态是一个输入框了,可以看到,你可以继续写完之后点回车,它就改写掉,那eidt指明的是到底哪一个单元可以进行修改。那它实际上它的值应该是一个他的行索引和列索引,就是行索引和真正这个行里面的这个index列索引,所以他应该是个二维数组,那他的状态肯定也在发生变化,有一点不同地方就不同。
然后这个search再说,我到底这边要不要去显示,刚才我们看到search这一行,很显然他也是个标志位,它在不断的发生变化。然后我们看到在search的时候,如果我真输进去的东西,比如输如lord,它只有这个lord出,这个指环王出现了,其他的没有了,就不出现。但是如果我把它点掉就不search了,它就会恢复出原来的样子,所以我们要有一个中间变量。
到底存之前是个啥状态,可能会疑惑不search不就是最初的数据嘛,但是还有一点是我们还可以去排序,就是在search的时候,那个数据还是那些数据,但它顺序可能有点变化,有没有专门用了一个变量来存储他,这些东西看起来都是在当前这个操作过程当中,他只可能会发生变化的东西,所以我们把它变成了一个state,就不是当前的这个,这个构建的一个props。我们去修改,然后要注意一个问题,我们现在没有这个例子,里面没有做更复杂的操作,所以它就属于这类的。
sort =(e) => {
let column =e.target.cellIndex;
let data = this.state.data.slice();
letdescending=this.state.sortby===column&& !this .state.descending
data.sort(compareFn:function(a:number,b:number){
return descending
?(a[column]<b[column]?1:-1)
:(a[column] >b[column]?1:-1);
});
this.setstate( state:{
data: data,
sortby: column,
descending: descending,
});
};
这段代码是在做排序,排序是在某一列升序或者降序,是一种实践,target.cell是系统封装的程序,e.target.cellIndex是在系统中点击的值,在系统内谁点击事件给你封装过来,这个e实际上是点击完以后有个实践,这个实践会给你传递过来。
那么如果要是说这个sort,它得到了这个事件之后,他就要先去知道你在谁上面在进行这个点击,那就得到了你在哪一列上点击,所以就表示在哪一列上。
slice这个函数是在说,我要把当前的这一个数组给他复制出来,一份赋给这个叫做data的一个局部变量,注意这个data和前面我们这里定义的,它不是一回事儿,这个data是在sort这个函数里面定义的,所以他只在sort这个函数里面有效,这个是state里的一个成员,那现在我们就得到了所有的数据,然后我们来看一看之前这个soft有没有排序。
如果他排序的之前就排序过,并且就是当前这一例。那么你在上面做一下点击的话。就意味着我要对这个排序,要么是他原来是大家看这里是降序,我再点一下它变成升序,再点一下就变成降序,所以呢,要看一下,那你原来是不是就在这个上头,去进行过设置,并且你这个原来是降序或者升序,这个结果作为这一次排序。他升序或者降序的一个依据,如果说原来不是在这一类上排的,这一条就不成立,他是false那它默认值就是false。
我们按升序排,你在一个没有点过的另外一列上,在当前这一列上再排序,在另外一个点,它默认就是让升序。如果你原来就在这一列上点的,并且原来就已经排过了,就把它取消,说明原来是降序,现在变升序,再降序升序,不是在一列上排,然后底下说把这个数据给sort一下,Sort是集合类,所谓集合类就是数组啊,列表啊,site,mab这些东西它的一个统称sort,就意味着他可以去把整个这个集合类里的所有元素去排个序。排序的时候可以用你给定的排序的函数来排序,那么我们给了一个排序的函数,就假设要传递A,B,两个值进去,那么我们要看你的这个升序还是降序的,通过代码来实现,而这个a和b是指的在data里面所有的元素,两两之间在比较中,一个是a一个是b。
取的在哪一列上去进行操作,把data里面每两行都拿出来进行比较的时候,出去取他们在排序的这一列上的这个值,他们进行比较,然后根据升序或降序的定义去判断到底是大于还是等于,这是个三元操作符,三元操作符,大家在C++或者Java里面都用过,就说他为true的时候就选上面,然后他为false就选下面,然后这个表达式里面他们又是一个三元操作,再判断两个,在讨论这一列上到底谁大谁小,那么处理完之后,这个数据就按照你想要的逻辑做了一个排序。那排序完了之后,其实这个页面是不会重绘的,那我们上节课说过触发页面重绘的唯一的就是用this.setstate。
就是你要去重绘要重新设置它的状态,设置状态的时候,这个数据这时候是指的是这个data,这个变量,然后sortbank就指的是一点的那一列,然后这个降序还是升序的,就是我们刚刚处理过的降序升序这个东西。这个setstate之后,这三个状态发生变化,那么我们知道一旦发生变化,他要触发render这个动作。
render =()=>{
return(
<div>
{this.renderToolbar()}
{this.renderTable()}
</div>
);};
就会去render ,render上面的工具条儿和底下的这个表。所以他就重复一次,重复一次就得到了我们想要的结果。这个重绘是的动作,这就是sort的逻辑。
接下来看这个show editor。
showEditor =(e) => {
this.setState( state: {
edit:{
row:parseInt(e.target.dataset.row, radix: 10), cell:e.target.cellIndex,
});};
在这个地方随便的双击就会有一个事件发生,会传递给你。做的事情就是说editor要让那个状态挨着状态,在表示,你当前要绘制哪一个,就是要去修改哪一个单元格的内容,因为它是一个数组,包含了它的行号和列号,那所以在show editor里面可以把这个状态给它改一下,就是改掉他的行和列它们的值,分别给改写的。
第一个是说我要拿到e,我们刚才说的是那个事件,事件标示点击的位置,现在的位置是table的表格,我们要在dataset里面取row,row可以解析dataset在哪一行做了动作,
后面数字10的意思是十进制,这里我们没有什么特殊要求按照十进制转换即可。即可得出在操作时,操作位置的行和列。
接下来看保存
save = (e)=>{
e.preventDefault();
let input = e.target.firstChild;
let data = this.state.data.slice();
datal[this.state.edit.row][this.state.edit.cell] = input.value; this.setstate( state:{
edit: null,
data: data,
});};};
首先按照正常情况这是一个input,input在回车之后一般有默认动作提交之类,接下来要获取target的firstchild,firstchild即input文本的数据,接下来要将之前确定的要改变行列的值告知input,这样可以将input的值将data中的替换掉,接下来调setstate重绘,输入回车,edit变为空,用替换掉的data代替data,这就是save做的动作。
toggleSearch = () => {
if(this.state.search) {
this.setstate( state:{
data:this.preSearchData,
search: false,
});
this.preSearchData = null;
} else {
this.preSearchData =this.state.data;
this.setstate( state:{
search: true,
});}};
Search做切换,这个函数的作用,判断这search空间的位置,要把search的东西保存起来,把search的标记变成true,如果已经在search状态,在表格页面点击,表格的内容将恢复到保存下来的状态,把search记成false,把中间变量记成空。
真正要做search的代码
search=(e)=> {
let needle = e.target.value.toLowerCase();
if(!needle) {
this.setState( state: {data: this.preSearchData});
return;
}
let idx = e.target.dataset.idx;
let searchdata =this.preSearchData.filter(function (row:T) {
return row[idx].toString().toLowerCase().index0f(needle) >-1
});
this.setstate( state: {data: searchdata});
};
首先需要知道输入的值并转为小写,如果输入的·东西被删掉,就恢复成search之前的状态,如果不是,马上return,如果输入东西,首先明确是在哪一列输入的,然后过滤没有search的所有原始数据,
过滤之后把它放入变量,用变量更新状态,过滤掉的数据不会出现,其余数据被保留,保留的数据赋给setstate,页面重绘,其中的逻辑是因为过滤当中的每一行,每一行将内容转化为字符串用来观察有没有needle,观察needle是否是字符串的子字符串,如果是将会返回该字符串在起始字符串的所有,如果不是就返回-1,只要大于-1就表示有这个东西就会被保留,否则不会被保留。
所以fuctum表示的是某一行是否被留下,true将被留下,false将不被留下,但是其中的问题就是,只在单独的一列搜索,并不能做联合的搜索,出来的结果在行上并没有很好的联合。
download(format,ev){
let contents = format === 'json'
?JsoN.stringify(this.state.data)
:this.state.data.reduce(function (result, row){
return result
+ row.reduce(function (rowresult, cell, idx) {
return rowresult
+’”’
+ cell.replace(/"/g,"…"')+::!
+’”’
+(idx<row.length -1? ',':'');
},'')
+ "\n";
},'');
let URL = window.URL II window.webkituRl;
let blob =new Blob(blobParts:[contentsl,options:{type:'text/' ev.target.href = URL.create0bjectURL(blob); ev.target.download ='data.' + format;
};
观察字符串是否为jasn,如果是jasn,直接用stringtry这个函数,如果不是将字符串转化为jasn字符串。组了一个字符串,把它放入contents,contents的内容变成flob中,二进制大对象,要把二进制大对象写出到data.jasn或者cs文件。
试一下可以发现,打开就是上面的表格。用文本编辑器把它打开,cs本质上是用逗号隔开的一行一行的数据,每个字段用双引号引起来。Reduce是对data里面所有的元素都做一下处理,处理完的结果是一个然后返回,返回在result。
Row是对每一行的元素进行处理,每一行要在每一列里进行操作,所以要有双引号,加cell里面做的全局替换,把转移字符恢复,如果是最后一列,什么不加,是最后一列要加都好,每一行做完要回车。
接下来是绘制工作。
Setstate工作的时候会触动render
renderToolbar = () => {
return (
<div className="toolbar">
<button onClick={this.toggleSearch}>Search</button>
<aonClick={this.download.bind(this,'json')} href="data.json">Export JSON</ a>
<a onClick={this.download.bind(this, 'csv')}
href="data.csv">Export CSV</ a>
</div>
);}:
Render分为工具栏和table,工具栏绘制的时候只需要绘制search,jasn,导出jasn,导出csv,csv search,就这三个按钮,前两个做成按钮,最后一个做成超链接。
无论怎样,在上面点击时都会有一个动作,这个动作要和刚刚的函数进行绑定,一带search就是切换search这个函数,就可以重绘之前函数状态,其他的两个都是在调download,他俩download的format一样,一个是jsan一个是csv。
下面是table,画table就是render search就是判断是否要文本框,当前状态不需要search,就什么都不画,如果当前状态需要search,要多加一行,无论在哪里操作都要调用search函数,判断在哪里做search,map是对表头,每一行字符都要生成一个td,这个td里面是input,input类型是text可以输入文本的,data/index,是在定义一个属性,输入data/的名字,就是data。输入文本框,文本框的内容属性被定义为idx。
就等于在处理哪一列,就是第几列的值,都是会导致属性变成idx。行需要row出来,列也是,后面有idx。这就可以把这一排画出来。最复杂的就说把rander table画出来。