1. state

1.1 概念理解

state是组件对象最重要的属性, 值是对象(可以包含多个kay-value的组合)
组件被称为状态机,通过更新组件的state来更新对应页面的显示(重新渲染组件

  1. import React from 'react'
  2. class App extends React.Component {
  3. constructor(props) {
  4. console.log(props) //此处props是{}
  5. super(props);
  6. this.state = {
  7. isHot:true
  8. };
  9. }
  10. changeWeather = () => {
  11. console.log(this) //此处的this是组件实例
  12. const {isHot} = this.state;
  13. this.setState({
  14. isHot: !isHot
  15. })
  16. }
  17. render() {
  18. const {isHot} = this.state;
  19. return (
  20. <div>
  21. <h2>今天的天气很:{isHot? '炎热':'凉爽'}</h2>
  22. <button onClick={this.changeWeather}>修改天气</button>
  23. </div>
  24. );
  25. }
  26. }
  27. export default App;

1.2 组件自定义的方法中this为何默认是undefined?

  • 构造器中的this组件实例对象;构造器中的this一定是当前实例对象;
  • render中的this也是组件实例对象;
  • 为啥自定义组件中的this是undefined?

看看这个问题:
类中所有定义的方法, 在局部默认开启了严格模式, 所以是undefined。

  1. <script>
  2. class Person{
  3. constructor(name, age){
  4. this.name = name;
  5. this.age = age;
  6. }
  7. study(){
  8. // study方法放在哪里?——类的原型对象上, 供实例使用
  9. // 通过Person实例调用study时,study中的this就是Person实例
  10. console.log(this)
  11. }
  12. }
  13. const p = new Person('Lisa',20)
  14. console.log(p) //Person {age: 20,name: "Lisa"}
  15. p.study()// this指向实例对象Person {age: 20,name: "Lisa"}
  16. //属于实例调用
  17. const m = p.study
  18. m() //undefined
  19. //属于直接调用
  20. // 类中所有定义的方法, 在局部默认开启了严格模式, 所以是undefined
  21. </script>

:::success changeWeather放在哪里?——组件实例的原型对象上, 供实例使用
由于changeWeather是作为onClick的回调, 所以不是直接通过实例调用的,是直接调用的
类中的方法默认开启了局部严格模式, 所以changeWeather中的thsi为undefined :::

  1. import React from 'react'
  2. class App extends React.Component {
  3. constructor(props) {
  4. console.log(props) //此处props是{}
  5. super(props);
  6. this.state = {
  7. isHot:true
  8. };
  9. }
  10. changeWeather () {
  11. console.log(this) //此处的this是undefined
  12. //此处的changeWeather放在哪里?——组件实例的原型对象上, 供实例使用
  13. // 由于changeWeather是作为onClick的回调, 所以不是直接通过实例调用的,是直接调用的
  14. // 类中的方法默认开启了局部严格模式, 所以changeWeather中的thsi为undefined
  15. // const {isHot} = this.state;
  16. // this.setState({
  17. // isHot: !isHot
  18. // })
  19. }
  20. render() {
  21. const {isHot} = this.state;
  22. return (
  23. <div>
  24. <h2>今天的天气很:{isHot? '炎热':'凉爽'}</h2>
  25. <button onClick={this.changeWeather}>修改天气</button>
  26. </div>
  27. );
  28. }
  29. }
  30. export default App;

1.3 修改this指向的方法

  1. 使用箭头函数 ```javascript changeWeather = () => { console.log(this) //此处的this是组件实例 const {isHot} = this.state; this.setState({ isHot: !isHot }) }

  1. 2. 在**模板**里面:**改变this指向**的方法
  2. ```javascript
  3. changeWeather () {
  4. console.log(this) //此处的this是组件实例
  5. const {isHot} = this.state;
  6. this.setState({
  7. isHot: !isHot
  8. })
  9. }
  10. <button onClick={this.changeWeather.bind(this)}>修改天气</button>
  1. 在调用的时候使用箭头函数绑定this

    1. changeWeather () {
    2. console.log(this) //此处的this是组件实例
    3. const {isHot} = this.state;
    4. this.setState({
    5. isHot: !isHot
    6. })
    7. }
    8. <button onClick={() => this.changeWeather()}>修改天气</button>
  2. 构造函数中使用bind绑定this

    1. import React from 'react'
    2. class App extends React.Component {
    3. constructor(props) {
    4. console.log(props) //此处props是{}
    5. super(props);
    6. this.state = {
    7. isHot:true
    8. };
    9. this.changeWeather = this.changeWeather.bind(this)
    10. }
    11. changeWeather () {
    12. console.log(this) //此处的this是组件实例
    13. const {isHot} = this.state;
    14. this.setState({
    15. isHot: !isHot
    16. })
    17. }
    18. render() {
    19. const {isHot} = this.state;
    20. return (
    21. <div>
    22. <h2>今天的天气很:{isHot? '炎热':'凉爽'}</h2>
    23. <button onClick={this.changeWeather}>修改天气</button>
    24. </div>
    25. );
    26. }
    27. }
    28. export default App;

    1.4 注意

    状态数据, 不能直接修改或更新,要借助React内置API:setState()

    1. this.state.isHot = true //这是错误写法

    状态必须经过setState进行修改, 且更新是一种合并, 不是替换。

    1. changeWeather () {
    2. const {isHot} = this.state;
    3. this.setState({
    4. isHot: !isHot
    5. })
    6. }

    构造器的作用:

    • 初始化状态
    • 修改自定义方法的this指向
      1. constructor(props) {
      2. console.log(props) //此处props是{}
      3. super(props);
      4. this.state = {
      5. isHot:true
      6. };
      7. this.changeWeather = this.changeWeather.bind(this)
      8. }

      1.5 state的简写方式:

      类中可以直接写赋值语句, 可以直接定义变量
      如果初始话的数据是从外部传进来的, 需要写构造器中。
      1. <script>
      2. class Person{
      3. constructor(name, age){
      4. this.name = name;
      5. this.age = age;
      6. }
      7. //类中可以直接写赋值语句, 可以直接定义变量
      8. address = '陕西省'
      9. }
      10. const p = new Person('Lisa',20)
      11. console.log(p) // Person {address: "陕西省",age: 20,name: "Lisa"}
      12. </script>
      1. import React from 'react'
      2. class App extends React.Component {
      3. state = {
      4. isHot:true
      5. };
      6. //赋值语句 + 箭头函数
      7. changeWeather = () => {
      8. const {isHot} = this.state;
      9. this.setState({
      10. isHot: !isHot
      11. })
      12. }
      13. render() {
      14. const {isHot} = this.state;
      15. return (
      16. <div>
      17. <h2>今天的天气很:{isHot? '炎热':'凉爽'}</h2>
      18. <button onClick={this.changeWeather}>修改天气</button>
      19. </div>
      20. );
      21. }
      22. }
      23. 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 (

    1. <h2>我是父组件</h2>
    2. <hr/>
    3. <Demo name = {name} age={age} sex={sex}/>

    ); } } export default App;

//子组件 import React from ‘react’ class Demo extends React.Component { render() { const {name, age, sex} = this.props return (

姓名:{name}

姓名:{age}

姓名:{sex}

); } }

export default Demo;

  1. <a name="M2bQ5"></a>
  2. ## 2.2 参数简写
  3. ```javascript
  4. import React from 'react'
  5. import Demo from './Demo'
  6. class App extends React.Component {
  7. state = {
  8. info:{
  9. name:'张三',
  10. age:20,
  11. sex:'boy'
  12. }
  13. };
  14. render() {
  15. const {info} = this.state;
  16. return (
  17. <div>
  18. <h2>我是父组件</h2>
  19. <hr/>
  20. <Demo name={info.name} age = {info.age} sex={info.sex}/>
  21. <p>等价于下面的写法</p>
  22. <Demo {...info}/>
  23. </div>
  24. );
  25. }
  26. }
  27. export default App;
  1. //展开运算符不能直接展开对象
  2. //不能直接这样:
  3. console.log(...p)
  4. //可以通过...赋值对象
  5. const p2 = {...p}
  6. //也可以直接修改字段得属性值
  7. let p3 = {...p, name:'Iric'}

:::success 在react里面可以直接展开得原因有2点:

  • 标签里面得{}consloe.log({…p})里面{}不一样, 标签里面得{}react规定得,
  • 在react里面, 标签里面可以展示对象, 是因为组件中引入babel.jsreact-dom了。 :::

    2.3 props传参限制

    什么是prop-types?prop代表父组件传递过来的值types代表类型。简单来说就是用来校验父组件传递过来值的类型 :::success 安装 npm install prop-types —save
    引入 import PropTypes from ‘prop-types’; ::: 参数限制

    1. //参数类型
    2. Person.propTypes = {
    3. name: PropTypes.string.isRequired,
    4. sex: PropTypes.string,
    5. age: PropTypes.number,
    6. run: PropTypes.func
    7. //因为String内置的是大写的, 这里为了区别都改为小写了。 但是function是一个关键字, 所以不能直接写function, 必须改为func.
    8. }
    9. //参数默认值
    10. Person.defaultProps = {
    11. sex:'男',
    12. age: 23
    13. }

    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 (

    我是父组件


    等价于下面的写法

    1. <Demo {...info}/>

    ); } } export default App;

//子组件 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;

  1. 可以设置多个类型
  2. ```javascript
  3. import PropTypes from 'prop-types';
  4. Demo.propTypes = {
  5. name: PropTypes.arrayOf(PropTypes.string, PropTypes.number) //可以设置多个类型
  6.    sex: PropTypes.oneOfType([PropTypes.string, PropTypes.number]) //符合任意一个类型即可
  7. }

也可以这样写:

  1. import React from 'react'
  2. import {PropTypes} from 'prop-types';
  3. class Demo extends React.Component {
  4. render() {
  5. const {name, age, sex} = this.props
  6. return (
  7. <div>
  8. <p>姓名:{name}</p>
  9. <p>姓名:{age}</p>
  10. <p>姓名:{sex}</p>
  11. </div>
  12. );
  13. }
  14. }
  15. //对标签属性进行类型和必要性的限制
  16. Demo.propsTypes = {
  17. name: PropTypes.string.isRequired, //name的类型是string类型,且是必传项
  18. sex: PropTypes.string, //sex是string类型,
  19. age: PropTypes.number,
  20. run: PropTypes.func
  21. }
  22. //指定默认标签属性值
  23. Demo.defaultProps = {
  24. sex:'男'
  25. }
  26. export default Demo;

2.5 函数式组件的传参限制

  1. import React from 'react';
  2. import {PropTypes } from 'prop-types';
  3. function Home(props) {
  4. const {name, age, sex} = props;
  5. return <ul>
  6. <li>姓名:{name}</li>
  7. <li>年龄:{age}</li>
  8. <li>性别:{sex}</li>
  9. </ul>
  10. }
  11. Home.propTypes={
  12. name:PropTypes.string.isRequired,
  13. age: PropTypes.number.isRequired,
  14. sex:PropTypes.string
  15. }
  16. Home.defaultProps = {
  17. sex:'girl'
  18. }
  19. export default Home;

3. Refs

3.1 字符串形式的ref

不建议使用, 因为会影响性能。后面会弃用。

  1. import React from 'react'
  2. class App extends React.Component {
  3. showData = () => {
  4. const {input1} = this.refs;
  5. alert(input1.value)
  6. }
  7. shouData2 = () => {
  8. const {input2} = this.refs;
  9. alert(input2.value)
  10. }
  11. render() {
  12. return (
  13. <div>
  14. <input ref = 'input1' placeholder="点击提示数据" type='text'/>
  15. <button type="" onClick={this.showData}>点击提示左侧的数据</button>
  16. <input ref = 'input2' onBlur={this.shouData2} type="text" placeholder='失去焦点提示右侧数据'/>
  17. </div>
  18. );
  19. }
  20. }
  21. export default App;

3.2 回调形式的ref

React 也支持一种设置refs的方式, 称为“回调refs”
回调ref: 传递一个函数, 这个函数中接受React组件实例或者HTML DOM元素作为参数, 以使他们能在其他地方被存储和访问

  1. import React from 'react';
  2. class Home extends React.Component {
  3. showData = () => {
  4. const { input1 } = this;
  5. console.log(input1.value)
  6. }
  7. showData2 = () => {
  8. const { input2 } = this;
  9. console.log(input2.value)
  10. }
  11. render() {
  12. return (
  13. <div>
  14. <input ref = {c => this.input1 = c} type="text" placeholder='点击显示左侧的值' />
  15. <button onClick={this.showData}>按钮</button>
  16. <input ref = {c => this.input2 = c } type="text" placeholder='失去焦点显示右侧的值' onBlur={this.showData2}/>
  17. </div>
  18. );
  19. }
  20. }
  21. export default Home;

ref回调次数问题:
第一次进去页面就会调用21行代码, 每次点击切换天气的时候也会调用ref.

  1. import React from 'react';
  2. class Home extends React.Component {
  3. state={
  4. isHot:false
  5. }
  6. changeWeather = () => {
  7. const { isHot} = this.state;
  8. this.setState({
  9. isHot: !isHot
  10. })
  11. }
  12. showData = () => {
  13. const { input1 } = this;
  14. console.log(input1.value)
  15. }
  16. render() {
  17. const {isHot} = this.state;
  18. return (
  19. <div>
  20. <h2>今天的天气很{isHot? '炎热':'凉爽'}</h2>
  21. <input ref = {(currentNode) => {this.input1 = currentNode; console.log(currentNode)}} type="text" placeholder='点击显示左侧的值' />
  22. <button onClick={this.showData}>按钮</button>
  23. <button onClick={this.changeWeather}>切换天气</button>
  24. </div>
  25. );
  26. }
  27. }
  28. export default Home;

官方总结: 如果ref回调时以内联函数的方式定义的, 在更新过程中,它会被执行2次。第一次传入参数null, 第二次传入参数DOM元素。 这是因为在每次渲染时会创建一个新的函数实例。所以React清空旧的ref设置新的。 通过将ref的回调函数定义成class的绑定函数的方式旧可以避免上述问题,但是大多数情况下是无关紧要的。

  1. import React from 'react'
  2. class App extends React.Component {
  3. showData = () => {
  4. const {input1} = this;
  5. alert(input1.value)
  6. }
  7. shouData2 = () => {
  8. const {input2} = this;
  9. alert(input2.value)
  10. }
  11. // 保存input
  12. saveInput2 = (e) => {
  13. this.input2 = e
  14. }
  15. render() {
  16. return (
  17. <div>
  18. {/* 内联函数的方式 */}
  19. <input ref = {e => this.input1 = e} placeholder="点击提示数据" type='text'/>
  20. <button type="" onClick={this.showData}>点击提示左侧的数据</button>
  21. {/* class绑定函数方式 */}
  22. <input ref = {this.saveInput2} onBlur={this.shouData2} type="text" placeholder='失去焦点提示右侧数据'/>
  23. </div>
  24. );
  25. }
  26. }
  27. export default App;

3. React.createRef()
React.createRef:调用后可以返回一个容器, 该容器可以存储被ref所表示的节点, 该容器是“专人专用”

  1. import React from 'react'
  2. class App extends React.Component {
  3. inputRef1 = React.createRef()
  4. inputRef2 = React.createRef()
  5. showData = () => {
  6. alert(this.inputRef1.current.value)
  7. }
  8. shouData2 = () => {
  9. alert(this.inputRef2.current.value)
  10. }
  11. render() {
  12. return (
  13. <div>
  14. <input ref = {this.inputRef1} placeholder="点击提示数据" type='text'/>
  15. <button type="" onClick={this.showData}>点击提示左侧的数据</button>
  16. <input ref = {this.inputRef2} onBlur={this.shouData2} type="text" placeholder='失去焦点提示右侧数据'/>
  17. </div>
  18. );
  19. }
  20. }
  21. export default App;