前端笔记之React(二)组件内部State&React实战&表单元素的受控
一、组件内部的State
1.1 state
state叫状态,是每一个类式组件都有的属性,但函数式组件,没有state。
state是一个对象,什么值都可以定义。
在任何类式组件的构造函数中,可以用this.state = {} 来给类的实例添加state属性,表示“状态”。
在render()函数的return中,可以用{this.state.a}插值来显示出每一个属性的值
import React from "react"; export default class App extends React.Component { constructor() { super(); //组件的内部状态,state属性 this.state = { a : 100 } } render() { return <div> <h1>{this.state.a}</h1> </div> } }
1.2 setState()
点击按钮之后,让100加1
import React from "react"; import ReactDOM from "react-dom"; export default class App extends React.Component { constructor() { super(); this.state = { a : 100 } } render() { return <div> <button onClick={()=>{ this.setState({ a : this.state.a + 1 }) }}>按我</button> <h1>{this.state.a}</h1> </div> } }
注意:
React中事件监听,要写在标签上。
事件名onClick而不是onclick,注意大写字母。因为React将事件名都进行了拓展,所以onClick是React自己的方法。同理,所有事件名on后面的首字母都是大写:onMouseEnter、onDoubleClick、onKeyDown。
onClick=后面紧跟{},表示插值。大括号中是一个箭头函数,这个函数必须是箭头函数,否则this错误。
<button onClick={()=>{ }}></button>
setState是定义在React.Component类中的方法,所以任何一个组件都能够无脑调用this.setState()。表示“设置state”。
this.setState({要设置的k : 新的v});
setState不仅能够改变实例的state的属性值,而且能产生视图刷新。而如果不用setState(),只是让state的属性值进行改变,视图是不刷新的。
错误的:
export default class App extends React.Component { constructor() { super(); this.state = { a : 100 } } render() { return <div> <button onClick={()=>{ this.state.a++; }}>按我</button> <h1>{this.state.a}</h1> </div> } }
1.3提炼出事件处理函数
之前的onClick后面直接跟上了{()=>{}},实际上可以提出来,封装成函数。
import React from "react"; export default class App extends React.Component{ constructor(){ super(); this.state = { a : 100 } } add(){ this.setState({ a:this.state.a + 1 }); } render(){ return <div> <h1>{this.state.a}</h1> <button onClick={()=>{this.add()}}>按我加1</button> <button onClick={this.add.bind(this)}>按我加1</button> </div> } };
方法的执行可以不通过箭头函数,但是不好,因为这样写不能传递参数
注意,提炼成为组件的方法(实际上写在了构造器的prototype上,实例的原型上),在onClick调用的时候,必须写bind(this),将调用的这个函数的上下文绑定为组件的实例。可以当做是一个固定的语法!
如何传参数?
import React from "react"; import ReactDOM from "react-dom"; export default class App extends React.Component { constructor() { super(); this.state = { a : 100 } } //单击事件的处理函数 add(n){ this.setState({ a : this.state.a + n }); } render() { return <div> <button onClick={()=>{this.add(1)}}>按我</button> <button onClick={()=>{this.add(2)}}>按我</button> <button onClick={()=>{this.add(3)}}>按我</button> <h1>{this.state.a}</h1> <div style={{ "width" : this.state.a + "px", "height": this.state.a + "px", "backgroundColor" : "orange", "transform" : "rotate(" + this.state.a + "deg)" }}></div> </div> } }
1.4 MVVM模式
React、Vue以及已经过时Angular都是MVVM模式,都有一个特点,就是:
数据驱动视图:数据变化了,视图自动变化
MVVM模式经典的4句话:
1)数据变化,视图就会自动变化
2)视图变化的原因,一定是数据变化了
3)数据是视图的本质
4)视图是数据的表现
我们以后再也不用关心DOM结构了,只关心数据,数据变化,视图自动变化
现在只需要用setState()来改变组件的实例的state,视图就会自动变化,后面你将知道,视图变化的原因是因为组件进入了新的生命周期,也将知道视图更新的效率因为有的Virtual DOM从而变的很快。
二、案例
2.1组件state的增删改查
知识点:html标签可以加ref(reference引用)属性,在组件内部可以通过this.refs来引用这个DOM元素。
<input type="text" ref="nameTxt"/>
获取标签的值:
var val = this.refs.nameTxt.value
import React from "react"; export default class App extends React.Component { constructor() { super(); this.state = { arr: [ { "id": 1, "name": "小明", "age": 12, "sex": "男" }, { "id": 2, "name": "小红", "age": 13, "sex": "女" }, { "id": 3, "name": "小刚", "age": 14, "sex": "男" }, { "id": 4, "name": "小白", "age": 15, "sex": "男" } ] } } //添加学员 addList(){ //获取值 var name = this.refs.nameTxt.value; var age = this.refs.ageTxt.value; var sex = this.refs.sexTxt.value; //改变state必须用setState()方法 this.setState({ arr:[ // 原来的项是不变 ...this.state.arr, // 只增加一项 { //id : 6 id:this.state.arr.reduce((a,b)=>{ return a.id > b.id ? a : b }).id + 1, name, age, sex } ] }) } //删除列表 delList(id){ this.setState({ arr:this.state.arr.filter(item=>item.id !=id) }); } render(){ return <div> <p>姓名:<input type="text" ref="nameTxt" /></p> <p>年龄:<input type="text" ref="ageTxt" /></p> <p>性别:<input type="text" ref="sexTxt" /></p> <button onClick={()=>{this.addList()}}>添加</button> <ul> { this.state.arr.map(item=>{ return <li key={item.id}> {item.id} -- {item.name}--年龄{item.age}岁,性别{item.sex} <button onClick={()=>{this.delList(item.id)}}>删除</button> </li> }) } </ul> </div> } }
React中都是纯函数编程。
2.2调色板
import React from "react"; export default class App extends React.Component{ constructor(){ super(); this.state = { r : 20, g : 200, b : 123 } } //变化颜色 setColor(k,v){ this.setState({ [k] : v }) } render(){ return <div> <div style={{ "width":"200px", "height":"200px", "background":`rgb(${this.state.r},${this.state.g},${this.state.b})`}}> </div> <p> <input type="range" max={255} value = {this.state.r} onChange={(e)=>{this.setColor("r",e.target.value)}} /> <span>{this.state.r}</span> </p> <p> <input type="range" max={255} value={this.state.g} onChange={(e)=>{this.setColor("g",e.target.value)}} /> <span>{this.state.g}</span> </p> <p> <input type="range" max={255} value={this.state.b} onChange={(e)=>{this.setColor("b",e.target.value)}} /> <span>{this.state.b}</span> </p> </div> } };
如果一个表单元素,和state的一个值进行了“关联”:
1)state的值就是表单元素的值;
2)改变表单元素,就会改变state的值。
我们叫做这个表单元素和数据进行了“双向数据绑定”,也叫作表单元素“受控”。
在React中,实现双向数据绑定(实现表单元素受控)的套路:
加上value属性实现从state中“要”值;
加上onChange事件实现“设置”state的值。
如果一个组件内部,所有表单元素都有state的数据,进行了双向数据绑定,此时称为“受控组件”。
<p> <input type="range" min={0} max={255} value={this.state.b} onChange={(e)=>{this.setColor("b" , e.target.value)}} /> </p>
简单的说“一个表单元素受控”,等价于“这个表单元素有一个值和他双向绑定”。
所有的表单元素受控,我们就说组件受控。
2.3微博发布框
结构:输入框、发布按钮、内容清空按钮,一串文字“当前88/140字”
当内容超过140字,则发布按钮不能点,文字变红
实时显示字数,当框中有内容,按钮可以点击清空
如果让一个元素是否使用某一个类,React官方建议安装classnames依赖
npm install --save classnames
import React from "react"; import classnames from "classnames"; export default class App extends React.Component{ constructor(){ super(); this.state = { txt: "" } } render(){ const length = this.state.txt.length; return <div> <textarea cols="30" rows="10" value={this.state.txt} onChange={(e)=>{this.setState({txt:e.target.value})}} > </textarea> <p> <button disabled={length == 0 || length>140}>发布</button> <button disabled={length==0} onClick={()=>{this.setState({txt:""})}}> 清空 </button> <span className={classnames({"danger":length > 140})}> 已写{length}/140字 </span> </p> </div> } };
三、表单元素的受控
什么是受控?
一个表单元素的value值和state中某个属性息息相关:
这个表单元素的值,来自于state中的属性
更改表单元素的值,能够更改state中的值
也叫双向数据绑定,不过React中称为“受控组件(Controller Component)”
Vue才叫双向数据绑定。
在React中,所有表单元素的受控方式一样,都是value={},onChange={}
3.1单行和多行文本框
import React from "react"; var classNames = require('classnames'); export default class App extends React.Component { //构造函数 constructor() { super(); this.state = { a : "我是默认的a值", b : 50, } } render() { return ( <div> <p> <input type="text" value={this.state.a} onChange={(e) => { this.setState({ a: e.target.value }) }} /> <span>{this.state.a}</span> </p> <p> <input type="range" value={this.state.b} onChange={(e) => { this.setState({ b: e.target.value })}} /> <span>{this.state.b}</span> </p> </div> ) } };
3.2下拉菜单
this.state = { c : "广州" }
<p> <select value={this.state.c} onChange={(e) => { this.setState({ c: e.target.value }) }} > <option value="广州">广州</option> <option value="深圳">深圳</option> <option value="佛山">佛山</option> <option value="东莞">东莞</option> <option value="云浮">云浮</option> </select> <span>{this.state.c}</span> </p>
3.3单选按钮
单选按钮受控的套路:checked={}、value="" 、onChange={}
<p> <input type="radio" name="sex" value="男" checked={this.state.e == '男'} onChange={(e) => { this.setState({ d: e.target.value }) }} />男 <input type="radio" name="sex" value="女" checked={this.state.e == '女'} onChange={(e) => { this.setState({ d: e.target.value })}} />女 <span>【{this.state.d}】</span> </p>
3.4复选框受控
复选框和上面所有表单元素都不一样:
要靠checked={}得到值,要判断includes
要写一个函数,传入将要验证的值,根据这个值是不是已经在数组中,再决定删除、添加。
setF(word){ if(this.state.f.includes(word)){ //如果这个值已经在f数组中,则删除 this.setState({ f : this.state.f.filter(item=>item != word) }); }else{ //如果这个值不在f数组中,则加入数组 this.setState({ f : [...this.state.f, word] }); } }
<p> 爱好: <input type="checkbox" value="看书" checked={this.state.f.includes('看书')} onChange={(e) => { this.setF("看书") }} />看书 <input type="checkbox" value="游泳" checked={this.state.f.includes('游泳')} onChange={(e) => { this.setF("游泳") }} />游泳 <input type="checkbox" value="打球" checked={this.state.f.includes('打球')} onChange={(e) => { this.setF("打球") }} />打球 <span>【{this.state.f.join(',')}】</span> </p>
3.5可选择的表单类-案例
这个案例学习DOM的上下树
控制元素是否显示或隐藏,不要用display属性
而是用三元运算符控制DOM元素是否上下树。如果上数写标签,如果不上树写null
不管是做什么案例,都是两大部分:①、写JSX侵入DOM标签,②、事件监听,改变state
import React from "react"; export default class App extends React.Component{ // 构造函数 constructor(){ super(); this.state = { arr:[ { "id": 1, "name": "小明", "age": 12, "sex": "男" }, { "id": 2, "name": "小红", "age": 13, "sex": "女" }, { "id": 3, "name": "小刚", "age": 14, "sex": "男" }, { "id": 4, "name": "小白", "age": 15, "sex": "男" } ], showCols:['姓名',"年龄","性别"] } } setChangeCols(word){ if(this.state.showCols.includes(word)){ this.setState({ showCols:this.state.showCols.filter(item=>item !=word) }) }else{ this.setState({ showCols:[ ...this.state.showCols, word ] }); }; } render(){ return <div> <div> <input type="checkbox" value="姓名" checked={this.state.showCols.includes("姓名")} onChange={(e)=>{this.setChangeCols("姓名")}} />姓名 <input type="checkbox" value="年龄" checked={this.state.showCols.includes("年龄")} onChange={(e)=>{this.setChangeCols("年龄")}} />年龄 <input type="checkbox" value="性别" checked={this.state.showCols.includes("性别")} onChange={(e)=>{this.setChangeCols("性别")}} />性别 </div> <span>{this.state.showCols}</span> <table> <tbody> <tr> <th>ID</th> {this.state.showCols.includes("姓名") ? <th>姓名</th> : null} {this.state.showCols.includes("年龄") ? <th>年龄</th> : null} {this.state.showCols.includes("性别") ? <th>性别</th> : null} </tr> { this.state.arr.map(item=>{ return <tr key={item.id}> <td>{item.id}</td> {this.state.showCols.includes("姓名") ? <td>{item.name}</td>:null} {this.state.showCols.includes("年龄") ? <td>{item.age}</td>:null} {this.state.showCols.includes("性别") ? <td>{item.sex}</td>:null} </tr> }) } </tbody> </table> </div> } };
3.6三级联动-案例
额外提供的数据:
[ { "name" : "广东省", "city" : [ { "name":"广州", "area":[ "天河区", "白云区", ... ] }, { "name":"深圳", "area":[ "福田区", "南山区" ] } .... ] }, .... ]
forEach、filter、map、reduce函数都是表达式,而不是语句体。if和for语句是语句体。
import React from "react"; import city from "./city.js"; export default class App extends React.Component{ constructor(){ super(); this.state = { "province":"广东省", "city":"广州市", "area":"天河区" } } render(){ //循环遍历省份 const showProvinces = ()=>{ //遍历city数据,提取每一项省份成为option var arr=[]; city.forEach((item,index)=>{ arr.push(<option key={index} value={item.name}>{item.name}</option>) }); return arr; } //循环遍历市,显示哪一个城市,需要根据state的province属性的省份选择城市 const showCitys = ()=>{ var arr = []; // 先筛选省份,再次筛选对应城市列表 var cityArr = city.filter(item=>item.name == this.state.province)[0].city; cityArr.forEach((item,index)=>{ arr.push(<option key={index} value={item.name}>{item.name}</option>) }); return arr; }; //显示区县 const showAreas = ()=>{ var arr = []; // 先筛选省份,再次筛选对应城市列表 var cityArr = city.filter(item=>item.name == this.state.province)[0].city; //根据市,得出区,只需选择区的第一项 var areaArr = cityArr.filter(item => item.name == this.state.city)[0].area //最后根据筛选出的市,遍历区 areaArr.forEach((item,index)=>{ arr.push(<option key={index} value={item}>{item}</option>) }) return arr; } return <div> 省份:<select value={this.state.province} onChange={(e)=>{ this.setState({ "province":e.target.value, "city":city.filter(item=>item.name == e.target.value)[0].city[0].name }) }} > {showProvinces()} </select> 城市:<select value={this.state.city} onChange={(e)=>{ this.setState({"city":e.target.value}) }} > {showCitys()} </select> 区县:<select value={this.state.area} onChange={(e)=>{ this.setState({"area":e.target.value}) }} > {showAreas()} </select> </div> } };
文章都是本人学习时的笔记整理,希望看完后能对您有所帮助,欢迎大家提意见,多多交流。
也有些文章是转载的,如果存在转载文章且没有标注转载地址的,请与我联系,马上处理。
自由转载-非商用-非衍生-保持署名。