前端笔记之React(二)组件内部State&React实战&表单元素的受控

时间:2022-04-04 06:11:46

一、组件内部的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>
}
}

前端笔记之React(二)组件内部State&React实战&表单元素的受控


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模式,都有一个特点,就是:

数据驱动视图:数据变化了,视图自动变化

前端笔记之React(二)组件内部State&React实战&表单元素的受控

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三级联动-案例

前端笔记之React(二)组件内部State&React实战&表单元素的受控

额外提供的数据:

[
{ "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>
}
};