react——组件

组件三大核心属性:

1.state

state的主要作用是用于组件保存 、控制、修改自己的状态。在组件内部初始化,可以被组件自身修改,而外部不能访问也不能修改。这里你可以认为state是一个局部的、只能被自身控制的数据源。state中状态可以通过this.setState方法进行更新,这会导致组件的重新渲染。

没有state的组件叫无状态组件,设置了state的叫有状态组件,因为状态会带来管理的复杂性,我们应该尽可能的多写无状态的组件,降低代码的维护程度,也会在一定程度上增强组件的复用性。

  • 组件中 render方法中的this为组件实例对象
  • 组件自定义方法中this为undefined,如何解决?
    1. 强制绑定 this;通过函数对象的 bind()
    2. 箭头函数
  • 状态数据,不能直接修改更新
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="test"></div>
<script type="text/javascript" src="../js/react.development.js"></script>
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel">
// 1.创建类式组件
class Weather extends React.Component {
state = {
isHot:true,
wind:'大风'
}
// 自定义方法 ————要用赋值语句的方法 + 箭头函数
changeWeather = () =>{
let isHot = this.state.isHot
this.setState({isHot:!isHot})
}
render(){
const {isHot,wind} = this.state
return <h1 id="title" onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'},{wind}</h1>
}
}
// 2.渲染组件到页面
ReactDOM.render(<Weather/>,document.getElementById('test'))
</script>

</body>
</html>

2.props

props的主要作用是让使用该组件的父组件可以传入参数来配置该组件,它是外部传进来的配置参数,组件内部无法控制也无法修改,除非 外部组件主动传入新的props,否则组件的props永远保持不变

补充扩展:展开运算符 ...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

let arr1 = [1,3,5,7,9]
let arr2 = [2,4,6,8,10]
// 展开一个数组
console.log(...arr1);
// 将多个数组进行连接
let arr3 = [...arr1,...arr2]

// 在函数中使用
function sum(...numbers) {
return numbers.reduce((preNum,currentNum) => {
return preNum + currentNum
})
}
console.log(sum(1, 2, 3, 4));

// 构造字面量对象时使用展开语法
let person = {name:'jerry',age:19}
let person2 = {...person}
console.log(...person);

// 复制对象得同时,改变对象得属性
let person3 = {...person,name:'jack',address:'地球'}

props批量传递以及对props进行类型限制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="test"></div>
<div id="test1"></div>
<div id="test2"></div>
<script type="text/javascript" src="../js/react.development.js"></script>
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/javascript" src="../js/prop-types.js"></script>
<script type="text/babel">
// 1.创建类式组件
class Person extends React.Component{
render(){
console.log(this)
const {name,age,sex} = this.props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age}</li>
</ul>
)
}
}
// 对标签属性进行类型、必要性的限制
Person.propTypes = {
name:PropTypes.string.isRequired, // 限制name必传,且为字符串
sex:PropTypes.string, // 限制sex为字符串
age:PropTypes.number, // 限制age为字数字
speak:PropTypes.func // 限制speak为函数
}
// 指定默认标签属性、
Person.defaultPrps = {
sex:'middle', //sex默认值为middle
age:19 // age默认值为19
}
// 渲染组件到页面
ReactDOM.render(<Person name="jerry" speak={speak} age={18} sex="男"/>,document.getElementById('test'))
ReactDOM.render(<Person name="tom" age={18} sex="男"/>,document.getElementById('test1'))

// 批量传递props
const p = {name:'sun',age:18,'sex':'female'}
ReactDOM.render(<Person {...p}/>,document.getElementById('test2'))

function speak() {
console.log('i am groot')
}
</script>

props的简写方式

  • props中的属性是只读的,不可以修改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Person extends React.Component {
// 对标签属性进行类型、必要性的限制
static propTypes = {
name: PropTypes.string.isRequired, // 限制name必传,且为字符串
sex: PropTypes.string, // 限制sex为字符串
age: PropTypes.number, // 限制age为字数字
speak: PropTypes.func // 限制speak为函数
}
// 指定默认标签属性、
static defaultProps = {
sex: 'middle', //sex默认值为middle
age: 19 // age默认值为19
}

render() {
console.log(this)
const {name, age, sex} = this.props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age}</li>
</ul>
)
}
}

函数式组件中使用props

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="test"></div>
<div id="test1"></div>
<div id="test2"></div>
<script type="text/javascript" src="../js/react.development.js"></script>
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/javascript" src="../js/prop-types.js"></script>
<script type="text/babel">
// 函数式组件
function Animal(props) {
const {name, age, sex} = props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age}</li>
</ul>
)
}

// 对标签属性进行类型、必要性的限制
Animal.propTypes = {
name: PropTypes.string.isRequired, // 限制name必传,且为字符串
sex: PropTypes.string, // 限制sex为字符串
age: PropTypes.number, // 限制age为字数字
speak: PropTypes.func // 限制speak为函数
}
// 指定默认标签属性、
Animal.defaultProps = {
sex: 'middle', //sex默认值为middle
age: 19 // age默认值为19
}

// 渲染组件到页面
ReactDOM.render(<Animal name="tommy"/>, document.getElementById('test1'))

</script>

3.refs

字符串形式的ref

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Dem0 extends React.Component {
// 展示左侧输入框框的数据
showData = () => {
const {input1} = this.refs
alert(input1.value)
}
showData2 = () => {
console.log(this)
const {input2} = this.refs
console.log(input2.value)
alert(input2.value)
}

render() {
return (
<div>
<input ref="input1" type="text" placeholder="点击按钮提示数据"/>&nbsp;
<button ref="button1" onClick={this.showData}>点击提示左侧的数据</button>
&nbsp;
<input ref="input2" onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>&nbsp;
</div>
)
}
}

回调形式的ref

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Demo1 extends React.Component {
// 展示左侧输入框框的数据
showData = () => {
const {input1} = this
alert(input1.value)
}
showData2 = () => {
console.log(this)
const {input2} = this

alert(input2.value)
}

render() {
return (
<div>
<input ref={c => this.input1 = c} type="text" placeholder="点击按钮提示数据"/>&nbsp;
<button ref="button1" onClick={this.showData}>点击提示左侧的数据</button>
<input onBlur={this.showData2} ref={c => this.input2 = c} type="text" placeholder="失去焦点提示数据"/>&nbsp;
</div>
)
}
}

回调形式ref调用次数的问题

  • 在使用函数式回调ref时,当组件state更新时,回调中会调用两次,第一次值为null ,第二次为该元素
  • 类的绑定形式的ref:将回调中的方法挂载到当前class上,则会避免这个问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 创建组件
class Demo extends React.Component{

state = {isHot:false}

showData = () => {
const {input1} = this
alert(input1.value)
}
changeWeather = () => {
// 获取原来的状态
const {isHot} = this.state
this.setState({isHot:!isHot})
}
saveInput = (c) => {
this.input1 = c
console.log("a",c)
}

render(){
const {isHot} = this.state
return (
<div>
<h1>今天天气很{isHot ? '炎热' : '凉爽'}</h1>
{/*<input ref={(currentNode) => {this.input1 = currentNode;console.log('@',currentNode)}} type="text"/>*/}
<input ref={this.saveInput} type="text"/>
<button onClick={this.showData} >点我提示输入的数据</button>
<button onClick={this.changeWeather}>切换天气</button>
</div>
)
}
}

createRef

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Demo1 extends React.Component {
// React.createRef 调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是专人专用的
myRef = React.createRef()

showData2 = () => {
console.log(this.myRef)
const {current} = this.myRef

alert(current.value)
}

render() {
return (
<div>
<input onBlur={this.showData2} ref={this.myRef} type="text" placeholder="失去焦点提示数据"/>&nbsp;
</div>
)
}
}

4.属性VS状态

相似点:都是纯js对象,都会触发render更新,都具有确定性(状态/属性相同,结果相同)

不同点:

  1. 属性能从父组件获取,状态不能

  2. 属性可以由父组件修改,状态不能

  3. 属性能在内部设置默认值,状态也可以,设置方式不一样

  4. 属性不在组件内部修改,状态要在组件内部修改

  5. 属性能设置子组件初始值,状态不可以

  6. 属性可以修改子组件的值,状态不可以

4.数据渲染

1.条件渲染

1
2
3
{
condition?'渲染列表的代码':'空空如也'
}

2.列表渲染

1
2
3
4
5
{
data.map(item=>
<li key={item.id}>{item.name}</li>
)
}

React的高效依赖于所谓的 Virtual-DOM,尽量不碰 DOM。对于列表元素来说会有一个问题:元素可能会在一个列表中改变位置。要实现这个操作,只需要交换一下 DOM 位置就行了,但是React并不知道其实我们只是改变了元素的位置,所以它会重新渲染后面两个元素(再执行 Virtual-DOM ),这样会大大增加 DOM 操作。但如果给每个元素加上唯一的标识,React 就可以知道这两个元素只是交换了位置,这个标识就是key,是每个元素的唯一标识。

3.dangerouslySetInnerHTML

处于安全的原因,React当中所有表达式的内容会被转义,如果直接输入,标签会被当成文本。这时候就需要使用dangerouslySetHTML属性,它允许我们动态设置innerHTML

1
2
3
4
5
6
7
8
9
import React, {useState} from 'react';
export default function Dangerous() {
const [data,setData] = useState("<h2>i am superman~</h2>")
return (
<div>
<div dangerouslySetInnerHTML={{__html:data}}/>
</div>
)
}