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.studym() //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指向**的方法```javascriptchangeWeather () {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 (
); } } export default App;<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 参数简写```javascriptimport 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 (
); } } export default App;我是父组件
等价于下面的写法
<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;
可以设置多个类型```javascriptimport 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.propsreturn (<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)}// 保存inputsaveInput2 = (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;
