初识React (二) 之组件三大核心属性

lxf2023-05-05 05:31:01

一、state

1. state 是组件对象最重要的属性, 值是对象(可以包含多个key-value的组合)
2. 组件被称为"状态机", 通过 setState 更新组件的 state 从而来更新对应的页面显示(重新渲染组件)
3. 每调用一次 setState,就会重新执行 render 函数,根据最新的 State 来创建 ReactElement 对象;然后再根据最新的 ReactElement 对象,对 DOM 进行修改

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>React</title>
</head>
<body>
    <div id="app"></div>

    <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
    <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>

    <script type="text/babel">
        class ShowAndHidden extends React.Component {
            constructor(props){
                super(props)

                this.state = {
                    isShow: true,
                    showStr : 'hello word!'
                }

            }

            triggle=()=>{
                this.setState({
                    isShow: !this.state.isShow
                }) 
            }


            render() {
                return (
                    <div>
                        {this.state.isShow && <p>{this.state.showStr}</p>}
                        <button onClick={this.triggle}>{this.state.isShow ? '隐藏': '显示'}</button>
                    </div>
                );
            }
        }

        ReactDOM.render(<ShowAndHidden />,document.getElementById('app'))
    </script>
</body>
</html>

初识React (二) 之组件三大核心属性

1. 若想要通过事件修改状态时,就需要使用到 setState 方法
2. 同步 or 异步

setState 方法到底是同步还是异步分两种情况

  • 在组件生命周期或React事件中,setState是异步, eg:
<script type="text/babel">
  class ShowAndHidden extends React.Component {
      constructor(props){
          super(props)
          this.state = {
              isShow: true,
              showStr : 'hello word!'
          }
      }
      
      triggle=()=>{
          this.setState({
              isShow: !this.state.isShow
          }) 
          console.log(this.state.isShow); // true
      }
      
      componentDidMount(){
          this.setState({showStr: '哈哈哈哈'})
          console.log('componentDidMount:',this.state.showStr) // 'componentDidMount:' hello word!
      }
      
      render() {
          return (
              <div>
                  <p>{this.state.showStr}</p>
                  <button onClick={this.triggle}>点击</button>
              </div>
          );
      }
  }
  ReactDOM.render(<ShowAndHidden />,document.getElementById('app'))
</script>

结果: 初识React (二) 之组件三大核心属性

此时在使用 setState 方法后,再打印 isShow 和 showStr,发现 isShow 和 showStr 没有被改变,由此可以看到 setState 是异步操作,在执行完 setState 之后不能够立刻拿到最新的state的结果

  • 在setTimeout或者原生dom事件中,setState是同步,eg:
<script type="text/babel">
    class ShowAndHidden extends React.Component {
        constructor(props){
            super(props)
            this.state = {
                isShow: true,
                showStr : 'hello word!'
            }
        }
        
        // setTimeout
        triggle=()=>{
            setTimeout(() => {
                this.setState({
                    showStr: "hhhh"
                });
                console.log('@@',this.state.showStr); // '@@' hhhh
            }, 0);
        }
        
        // 原生DOM事件
        componentDidMount() {
            const btn = this.refs.btn;
            btn.addEventListener('click', () => {
              this.setState({
                showStr: "abcd"
              });
              console.log(this.state.showStr); // abcd
            })
        }
        render() {
            return (
                <div>
                    <p>{this.state.showStr}</p>
                    <button onClick={this.triggle}>点击</button>  
                    <button ref="btn">点击btn</button>
                </div>
            );
        }
    }
    ReactDOM.render(<ShowAndHidden />,document.getElementById('app'))
</script>

结果: 初识React (二) 之组件三大核心属性

3. 如何获取更新后的值
  1. 上面两种在setTimeout或者原生dom事件中
  2. setState 接受两个参数:第二个参数是一个回调函数,这个回调函数会在更新后会执行
triggle=()=>{
    this.setState({
            showStr: "hhhh"
    },()=>{
        console.log('@@',this.state.showStr); // hhhh
    });
}
4. setState的合并
class ShowAndHidden extends React.Component {
    constructor(props){
        super(props)
        this.state = {
            isShow: true,
            showStr : 'hello word!'
        }
    }
    
    triggle=()=>{
        this.setState({
                showStr: 'hhhh'
        },()=>{
            console.log('@@',this.state); // @@ {isShow: true, showStr: 'hhhh'}
        });
    }
    // 通过setState修改showStr,不会对 isShow 产生影响的
    
    render() {
        return (
            <div>
                <p>{this.state.showStr}</p>
                <button onClick={this.triggle}>点击</button> 
                <button onClick={this.handleClick}>点击1</button> 
            </div>
        );
    }
}
// 多个 setState 合并
handleClick=()=>{
    this.setState({
            showStr: '哈哈'
    },()=>{
        console.log('@@',this.state); // @@ {isShow: true, showStr: '哈哈哈哈'}
    });
    this.setState({
            showStr: '哈哈哈'
    },()=>{
        console.log('@@',this.state); // @@ {isShow: true, showStr: '哈哈哈哈'}
    });
    this.setState({
            showStr: '哈哈哈哈'
    },()=>{
        console.log('@@',this.state); // @@ {isShow: true, showStr: '哈哈哈哈'}
    });
}

render() {
    return (
        <div>
            <button onClick={this.handleClick}>点击</button>  
        </div>
    );
}
若要执行多次,setState 传入一个函数 返回一个对象
handleClick=()=>{
    this.setState((state,props)=>{
        return {
            num: state.num+1
        }
    },);
    this.setState((state,props)=>{
        return {
            num: state.num+1
        }
    });
    this.setState((state,props)=>{
        return {
            num: state.num+1
        }
    },()=>{
        console.log('@@',this.state.num); 
    });
}
render() {
    return (
        <div>
            <button onClick={this.handleClick}>点击</button>  
        </div>
    );
}

初识React (二) 之组件三大核心属性

5. 总结:

setState 为什么要异步更新?

  1. 可以显著提升性能,如果每次调用 setState 都进行一次更新,render 函数会被频繁调用,界面重新渲染,导致效率低,所以获取到多个更新,然后进行批量的更新
  2. 如果同步更新了 state,但是还没有执行 render 函数,那么 state 和 props 不能保持同步

二、props

1. 每个组件对象都会有props(properties的简写)属性
2. 组件标签的所有属性都保存在props中

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>React</title>
</head>
<body>
    <div id="app"></div>

    <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
    <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>

    <script type="text/babel">
        class Person extends React.Component {
            constructor(props){
                super(props)

                console.log(this.props); // {name: '前端代码仔', age: 100, sex: '男'}
                console.log(this.props.name); // 前端代码仔
            }

            render() {
                return (
                    <div>
                        <h1>props</h1>
                    </div>
                )
            }
        }

        ReactDOM.render(<Person name='前端代码仔' age={100} sex='男'/>,document.getElementById('app'))
    </script>
</body>
</html>

初识React (二) 之组件三大核心属性

1. 通过标签属性从组件外向组件内传递变化的数据 或 使用扩展属性: 将对象的所有属性通过props传递
<Person name='前端代码仔' age={100} sex='男'/>
或
const person = {
    name: '前端代码仔',
    age: 18,
    sex: '男'
}
ReactDOM.render(<Person {...person}/>,document.getElementById('app'))
2. 内部读取某个属性 this.props.属性名 eg: this.props.name
3. 对 props 中的属性值进行类型限制和必要性限制
// 第一种方式(React v15.5 开始已弃用)
Person.propTypes = {
    name: React.PropTypes.string.isRequired,  
    age: React.PropTypes.number,
    sex: React.PropTypes.string
}
或
// 第二种方式 使用prop-types库进限制(需要引入prop-types库)
Person.propTypes = {
    name: PropTypes.string.isRequired,  
    age: PropTypes.number,
    sex: PropTypes.string
}
4. 默认属性值
Person.defaultProps = {
    name: '前端仔'
    age: 18,  
    sex: '男 

注意!!! 组件内部不要修改props数据; 函数类型限制要用 PropTypes.func

三、refs

组件内的标签可以定义ref属性来标识自己

1. 字符串形式的ref
<button ref="btn">点击btn</button>
2. 回调形式的ref
<button ref={(currentNode)=> {this.btn = currentNode} }>点击btn</button>
3. createRef 创建 ref 容器
btn = React.createRef()
<button ref={this.btn} >点击btn</button>