1. state
1.1 概念理解
state是组件对象最重要的属性, 值是对象(可以包含多个kay-value的组合)
组件被称为状态机,通过更新组件的state来更新对应页面的显示(重新渲染组件)
import React from 'react'
class App extends React.Component {
constructor(props) {
console.log(props) //此处props是{}
super(props);
this.state = {
isHot:true
};
}
changeWeather = () => {
console.log(this) //此处的this是组件实例
const {isHot} = this.state;
this.setState({
isHot: !isHot
})
}
render() {
const {isHot} = this.state;
return (
<div>
<h2>今天的天气很:{isHot? '炎热':'凉爽'}</h2>
<button onClick={this.changeWeather}>修改天气</button>
</div>
);
}
}
export default App;
1.2 组件自定义的方法中this为何默认是undefined?
- 构造器中的this是组件实例对象;构造器中的this一定是当前实例对象;
- render中的this也是组件实例对象;
- 为啥自定义组件中的this是undefined?
看看这个问题:
类中所有定义的方法, 在局部默认开启了严格模式, 所以是undefined。
<script>
class Person{
constructor(name, age){
this.name = name;
this.age = age;
}
study(){
// study方法放在哪里?——类的原型对象上, 供实例使用
// 通过Person实例调用study时,study中的this就是Person实例
console.log(this)
}
}
const p = new Person('Lisa',20)
console.log(p) //Person {age: 20,name: "Lisa"}
p.study()// this指向实例对象Person {age: 20,name: "Lisa"}
//属于实例调用
const m = p.study
m() //undefined
//属于直接调用
// 类中所有定义的方法, 在局部默认开启了严格模式, 所以是undefined
</script>
:::success
changeWeather放在哪里?——组件实例的原型对象上, 供实例使用
由于changeWeather是作为onClick的回调, 所以不是直接通过实例调用的,是直接调用的
类中的方法默认开启了局部严格模式, 所以changeWeather中的thsi为undefined
:::
import React from 'react'
class App extends React.Component {
constructor(props) {
console.log(props) //此处props是{}
super(props);
this.state = {
isHot:true
};
}
changeWeather () {
console.log(this) //此处的this是undefined
//此处的changeWeather放在哪里?——组件实例的原型对象上, 供实例使用
// 由于changeWeather是作为onClick的回调, 所以不是直接通过实例调用的,是直接调用的
// 类中的方法默认开启了局部严格模式, 所以changeWeather中的thsi为undefined
// const {isHot} = this.state;
// this.setState({
// isHot: !isHot
// })
}
render() {
const {isHot} = this.state;
return (
<div>
<h2>今天的天气很:{isHot? '炎热':'凉爽'}</h2>
<button onClick={this.changeWeather}>修改天气</button>
</div>
);
}
}
export default App;
1.3 修改this指向的方法
- 使用箭头函数 ```javascript changeWeather = () => { console.log(this) //此处的this是组件实例 const {isHot} = this.state; this.setState({ isHot: !isHot }) }
2. 在**模板**里面:**改变this指向**的方法
```javascript
changeWeather () {
console.log(this) //此处的this是组件实例
const {isHot} = this.state;
this.setState({
isHot: !isHot
})
}
<button onClick={this.changeWeather.bind(this)}>修改天气</button>
在调用的时候使用箭头函数绑定this
changeWeather () {
console.log(this) //此处的this是组件实例
const {isHot} = this.state;
this.setState({
isHot: !isHot
})
}
<button onClick={() => this.changeWeather()}>修改天气</button>
在构造函数中使用bind绑定this
import React from 'react'
class App extends React.Component {
constructor(props) {
console.log(props) //此处props是{}
super(props);
this.state = {
isHot:true
};
this.changeWeather = this.changeWeather.bind(this)
}
changeWeather () {
console.log(this) //此处的this是组件实例
const {isHot} = this.state;
this.setState({
isHot: !isHot
})
}
render() {
const {isHot} = this.state;
return (
<div>
<h2>今天的天气很:{isHot? '炎热':'凉爽'}</h2>
<button onClick={this.changeWeather}>修改天气</button>
</div>
);
}
}
export default App;
1.4 注意
状态数据, 不能直接修改或更新,要借助React内置API:setState()
this.state.isHot = true //这是错误写法
状态必须经过setState进行修改, 且更新是一种合并, 不是替换。
changeWeather () {
const {isHot} = this.state;
this.setState({
isHot: !isHot
})
}
构造器的作用:
- 初始化状态
- 修改自定义方法的this指向
constructor(props) {
console.log(props) //此处props是{}
super(props);
this.state = {
isHot:true
};
this.changeWeather = this.changeWeather.bind(this)
}
1.5 state的简写方式:
类中可以直接写赋值语句, 可以直接定义变量
如果初始话的数据是从外部传进来的, 需要写构造器中。<script>
class Person{
constructor(name, age){
this.name = name;
this.age = age;
}
//类中可以直接写赋值语句, 可以直接定义变量
address = '陕西省'
}
const p = new Person('Lisa',20)
console.log(p) // Person {address: "陕西省",age: 20,name: "Lisa"}
</script>
import React from 'react'
class App extends React.Component {
state = {
isHot:true
};
//赋值语句 + 箭头函数
changeWeather = () => {
const {isHot} = this.state;
this.setState({
isHot: !isHot
})
}
render() {
const {isHot} = this.state;
return (
<div>
<h2>今天的天气很:{isHot? '炎热':'凉爽'}</h2>
<button onClick={this.changeWeather}>修改天气</button>
</div>
);
}
}
export default App;
2. props
2.1 组件之间传值
```javascript //父组件 import React from ‘react’ import Demo from ‘./Demo’ class App extends React.Component { state = { info:{ name:’张三’, age:20, sex:’boy’ } };
render() { const {name, age, sex} = this.state.info; return (
<h2>我是父组件</h2>
<hr/>
<Demo name = {name} age={age} sex={sex}/>
//子组件 import React from ‘react’ class Demo extends React.Component { render() { const {name, age, sex} = this.props return (
姓名:{name}
姓名:{age}
姓名:{sex}
export default Demo;
<a name="M2bQ5"></a>
## 2.2 参数简写
```javascript
import React from 'react'
import Demo from './Demo'
class App extends React.Component {
state = {
info:{
name:'张三',
age:20,
sex:'boy'
}
};
render() {
const {info} = this.state;
return (
<div>
<h2>我是父组件</h2>
<hr/>
<Demo name={info.name} age = {info.age} sex={info.sex}/>
<p>等价于下面的写法</p>
<Demo {...info}/>
</div>
);
}
}
export default App;
//展开运算符不能直接展开对象
//不能直接这样:
console.log(...p)
//可以通过...赋值对象
const p2 = {...p}
//也可以直接修改字段得属性值
let p3 = {...p, name:'Iric'}
:::success 在react里面可以直接展开得原因有2点:
- 标签里面得{}跟consloe.log({…p})里面{}不一样, 标签里面得{}是react规定得,
在react里面, 标签里面可以展示对象, 是因为组件中引入babel.js和react-dom了。 :::
2.3 props传参限制
什么是prop-types?prop代表父组件传递过来的值,types代表类型。简单来说就是用来校验父组件传递过来值的类型 :::success 安装 npm install prop-types —save
引入 import PropTypes from ‘prop-types’; ::: 参数限制//参数类型
Person.propTypes = {
name: PropTypes.string.isRequired,
sex: PropTypes.string,
age: PropTypes.number,
run: PropTypes.func
//因为String内置的是大写的, 这里为了区别都改为小写了。 但是function是一个关键字, 所以不能直接写function, 必须改为func.
}
//参数默认值
Person.defaultProps = {
sex:'男',
age: 23
}
2.4 类组件的传参限制
```javascript //父组件 import React from ‘react’ import Demo from ‘./Demo’ class App extends React.Component { state = { info:{ name:’张三’, age:20, sex:’boy’ } };
render() { const {info} = this.state; return (
我是父组件
等价于下面的写法
<Demo {...info}/>
//子组件 import React from ‘react’ import {PropTypes } from ‘prop-types’; class Demo extends React.Component { //对标签属性进行类型和必要性的限制 static propsTypes = { name: PropTypes.string.isRequired, //name的类型是string类型,且是必传项 sex: PropTypes.string, //sex是string类型, age: PropTypes.number, run: PropTypes.func } //指定默认标签属性值 static defaultProps = { sex:’男’ } render() { const {name, age, sex} = this.props return (
姓名:{name}
姓名:{age}
姓名:{sex}
export default Demo;
可以设置多个类型
```javascript
import PropTypes from 'prop-types';
Demo.propTypes = {
name: PropTypes.arrayOf(PropTypes.string, PropTypes.number) //可以设置多个类型
sex: PropTypes.oneOfType([PropTypes.string, PropTypes.number]) //符合任意一个类型即可
}
也可以这样写:
import React from 'react'
import {PropTypes} from 'prop-types';
class Demo extends React.Component {
render() {
const {name, age, sex} = this.props
return (
<div>
<p>姓名:{name}</p>
<p>姓名:{age}</p>
<p>姓名:{sex}</p>
</div>
);
}
}
//对标签属性进行类型和必要性的限制
Demo.propsTypes = {
name: PropTypes.string.isRequired, //name的类型是string类型,且是必传项
sex: PropTypes.string, //sex是string类型,
age: PropTypes.number,
run: PropTypes.func
}
//指定默认标签属性值
Demo.defaultProps = {
sex:'男'
}
export default Demo;
2.5 函数式组件的传参限制
import React from 'react';
import {PropTypes } from 'prop-types';
function Home(props) {
const {name, age, sex} = props;
return <ul>
<li>姓名:{name}</li>
<li>年龄:{age}</li>
<li>性别:{sex}</li>
</ul>
}
Home.propTypes={
name:PropTypes.string.isRequired,
age: PropTypes.number.isRequired,
sex:PropTypes.string
}
Home.defaultProps = {
sex:'girl'
}
export default Home;
3. Refs
3.1 字符串形式的ref
不建议使用, 因为会影响性能。后面会弃用。
import React from 'react'
class App extends React.Component {
showData = () => {
const {input1} = this.refs;
alert(input1.value)
}
shouData2 = () => {
const {input2} = this.refs;
alert(input2.value)
}
render() {
return (
<div>
<input ref = 'input1' placeholder="点击提示数据" type='text'/>
<button type="" onClick={this.showData}>点击提示左侧的数据</button>
<input ref = 'input2' onBlur={this.shouData2} type="text" placeholder='失去焦点提示右侧数据'/>
</div>
);
}
}
export default App;
3.2 回调形式的ref
React 也支持一种设置refs的方式, 称为“回调refs”。
回调ref: 传递一个函数, 这个函数中接受React组件实例或者HTML DOM元素作为参数, 以使他们能在其他地方被存储和访问
import React from 'react';
class Home extends React.Component {
showData = () => {
const { input1 } = this;
console.log(input1.value)
}
showData2 = () => {
const { input2 } = this;
console.log(input2.value)
}
render() {
return (
<div>
<input ref = {c => this.input1 = c} type="text" placeholder='点击显示左侧的值' />
<button onClick={this.showData}>按钮</button>
<input ref = {c => this.input2 = c } type="text" placeholder='失去焦点显示右侧的值' onBlur={this.showData2}/>
</div>
);
}
}
export default Home;
ref回调次数问题:
第一次进去页面就会调用21行代码, 每次点击切换天气的时候也会调用ref.
import React from 'react';
class Home extends React.Component {
state={
isHot:false
}
changeWeather = () => {
const { isHot} = this.state;
this.setState({
isHot: !isHot
})
}
showData = () => {
const { input1 } = this;
console.log(input1.value)
}
render() {
const {isHot} = this.state;
return (
<div>
<h2>今天的天气很{isHot? '炎热':'凉爽'}</h2>
<input ref = {(currentNode) => {this.input1 = currentNode; console.log(currentNode)}} type="text" placeholder='点击显示左侧的值' />
<button onClick={this.showData}>按钮</button>
<button onClick={this.changeWeather}>切换天气</button>
</div>
);
}
}
export default Home;
官方总结: 如果ref回调时以内联函数的方式定义的, 在更新过程中,它会被执行2次。第一次传入参数null, 第二次传入参数DOM元素。 这是因为在每次渲染时会创建一个新的函数实例。所以React清空旧的ref并设置新的。 通过将ref的回调函数定义成class的绑定函数的方式旧可以避免上述问题,但是大多数情况下是无关紧要的。
import React from 'react'
class App extends React.Component {
showData = () => {
const {input1} = this;
alert(input1.value)
}
shouData2 = () => {
const {input2} = this;
alert(input2.value)
}
// 保存input
saveInput2 = (e) => {
this.input2 = e
}
render() {
return (
<div>
{/* 内联函数的方式 */}
<input ref = {e => this.input1 = e} placeholder="点击提示数据" type='text'/>
<button type="" onClick={this.showData}>点击提示左侧的数据</button>
{/* class绑定函数方式 */}
<input ref = {this.saveInput2} onBlur={this.shouData2} type="text" placeholder='失去焦点提示右侧数据'/>
</div>
);
}
}
export default App;
3. React.createRef()
React.createRef:调用后可以返回一个容器, 该容器可以存储被ref所表示的节点, 该容器是“专人专用”。
import React from 'react'
class App extends React.Component {
inputRef1 = React.createRef()
inputRef2 = React.createRef()
showData = () => {
alert(this.inputRef1.current.value)
}
shouData2 = () => {
alert(this.inputRef2.current.value)
}
render() {
return (
<div>
<input ref = {this.inputRef1} placeholder="点击提示数据" type='text'/>
<button type="" onClick={this.showData}>点击提示左侧的数据</button>
<input ref = {this.inputRef2} onBlur={this.shouData2} type="text" placeholder='失去焦点提示右侧数据'/>
</div>
);
}
}
export default App;