React 之 组件实例的三大核心属性 ,state props ref

扩展:

在React中,构造函数仅用于以下两种情况

1)通过给this.state 赋值对象来初始化内部 state

2)为事件处理函数绑定实例

在构造函数中注意事项:

在为ReactComponent子类实现构造函数时,应该其他语句之前调用super(props).否则this.props在构造函数中会出现未定义的bug

说人话(翻译)即:传不传props,就看你要不要使用props

// constructor(props){
      //   super(props)
      //   console.log(props)     // props和this.props数据是一样的
      //   console.log(this.props)
      // }
      // constructor()
      // {
      //   super()
      //   console.log(this.props);  //undefined
      // }
      constructor(props)
      {
        super()
        console.log(this.props)  //undefined
      }

一.初识 state

1)state是组件对象醉重要的属性,值是对象(可以包含多个键值对数据)

2)组件被称为"状态机",通过更新组件的state来更新对应的页面显示(重新渲染组件)

强烈注意:

1)组件中render方法中的this为组件实例对象

2)组件自定义的方法中,this为undefined,如何解决?

a.强制绑定this,通过函数对象的bind()

b.箭头函数

3)状态数据,不能直接修改或更新

初始化state状态 案例 (绑定事件)

class Weather extends React.Component{
      constructor(){
        super()
        this.state = {isHot:true,wind:'大风'}

      }
      render(){
        console.log(this)
        return <h1 onClick={demo}>今天天气很 {this.state.isHot?'炎热':'凉爽'}</h1>
      }
    }
    ReactDOM.render(<Weather/>,document.getElementById('test'))

    function demo(){
console.log(this) // undefined console.log('标题被点击了'); } 如何事件绑定,如上代码
扩展: 原生js有哪几种事件绑定方式
1)节点.addEventListener('click',()=>{ alert('点击了')})
2)节点.onclick = ()=>{}
3)<button onclick="demo()">

变更类中this的指向问题

// 创建组件
    class Weather extends React.Component{
      constructor(){
        super()
        this.state = {isHot:true,wind:'大风'}  //通过给state赋值对象来初始化内部state
        this.changeWeather = this.changeWeather.bind(this)   //为事件处理函数绑定实例

      }
      render(){
        console.log(this);
        this.changeWeather()
        return <h1 onClick={this.changeWeather}>今天天气很 {this.state.isHot?'炎热':'凉爽'}</h1>
      }
      changeWeather(){
        console.log(this);
      }
    }
    ReactDOM.render(<Weather/>,document.getElementById('test'))

如何修改 state的数据? 通过setState

class Weather extends React.Component{
      constructor(props){
        /* constructor 调用了几次
          只有组件实例化时,才会执行一次,这里只有一次
        */
        console.log('constructor') 
        super(props)
        this.state = {isHot:false}
        this.changeWeather = this.changeWeather.bind(this)

      }
      render(){
        /* render 调用了几次?
          执行了1+n次   实例化执行了一次,然后状态发生变化时就会执行
        */
        console.log('render')
        return <h1 onClick={this.changeWeather}>今天天气很 {this.state.isHot?'炎热':'凉爽'}</h1>
      }
      changeWeather(){
        console.log('changeWeather')
        const {isHot} = this.state
        this.setState({isHot:!isHot})
        // console.log(this);
      }
    }
    ReactDOM.render(<Weather/>,document.getElementById('test'))

简化写法

// 创建组件
    class Weather extends React.Component{
      // 初始化状态
      state = {isHot:false}

      render(){
        const {isHot} = this.state
        return <h1 onClick={this.changeWeather}>今天天气很 {isHot?'炎热':'凉爽'}</h1>
      }
      //自定义方法
      changeWeather = ()=>{
        const {isHot} = this.state
        this.setState({isHot:!isHot})
      }
    }
    // 渲染组件到页面上
    ReactDOM.render(<Weather/>,document.getElementById('test'))

二.初始props 是只读的

class Person extends React.Component {
      render() {
        return (
          <ul>
            <li>姓名:{this.props.name}</li>
            <li>性别:男</li>
            <li>年龄:{this.props.age}</li>
          </ul>
        )
      }
    }
const p = {name:'张三',}
ReactDOM.render(<Person name="tom" age="20"/>,document.getElementById('test1'))
ReactDOM.render(<Person name="Jon" age="18"/>,document.getElementById('test1'))

优化写法

class Person extends React.Component {
      render() {
        let {name,age,gender} =this.props
        return (
          <ul>
            <li>姓名:{this.props.name}</li>
            <li>性别:{this.props.gender}</li>
            <li>年龄:{this.props.age}</li>
          </ul>
        )
      }
    }
    const p = {name:'张三',age:20,gender:'男'}
    // ReactDOM.render(<Person name="tom" age="20"/>,document.getElementById('test1'))
    ReactDOM.render(<Person {...p}/>,document.getElementById('test1'))

对props进行限制

注意:需要引用 prop-types.js 因为在15.几的时候这样使用的,但是16.几之后,React.PropTypes.string.isRequired就被修改了

具体原因看下面代码块中的(tip)

class Person extends React.Component {
      render() {
        let {name,age,gender} =this.props
        return (
          <ul>
            <li>姓名:{this.props.name}</li>
            <li>性别:{this.props.gender}</li>
            <li>年龄:{this.props.age}</li>
          </ul>
        )
      }
    }
    Person.propTypes = {   //propTypes 小写 react就会识别是要进行prop限制
//name:React.PropTypes.string.isRequired, // tips:版本15.几的时候把这种功能添加到了react.development.js上,变的笨重 name:PropTypes.string.isRequired, //PropTypes 大写 即:数据类型 gender:PropTypes.string } Person.defaultProps = { //默认值 gender:'不难不女', name:'李四' } const p = {name:'张三',age:20} // ReactDOM.render(<Person name="tom" age="20"/>,document.getElementById('test1')) ReactDOM.render(<Person {...p}/>,document.getElementById('test1'))

props的简写方式

class Person extends React.Component {
      static propTypes = {
        name:PropTypes.string.isRequired,
        gender:PropTypes.string,
        age:PropTypes.number
      }
      static defaultProps = {
        gender:'男',
        age:18
      }
      render(){
        console.log(this)
        const {name,age,gender} = this.props
        return (
          <ul>
            <li>名字:{name}</li>
            <li>年纪:{age}</li>
            <li>性别:{gender}</li>
          </ul>
        )
      }
    }
    const p= {name:'李四',age:20,gender:'男'}
    ReactDOM.render(<Person name="张三"  gender="女"/>,document.getElementById('test1'))
    ReactDOM.render(<Person {...p }/>,document.getElementById('test2'))

类组件中 构造器和props

注:看扩展内容

函数组件使用 props (函数组件只能玩转props)

三.初始ref 最推荐第三种 React.createRef()

第一种:String 类型的Refs ,在未来会被移除 简单案例(了解即可)

原因:string类型refs存在一些问题,太多了就会效率不高问题

class Demo extends React.Component {
        render(){
          return (
            <div>
              <input type="text" ref="input1" placeholder="点击按钮提示数据"/>  
              <button onClick={this.handleClick}>点我提示左侧的数据</button> 
              <input type="text" ref="input2" placeholder="失去焦点提示数据"  onBlur={this.handleBlur}/>
            </div>
          )
        }
        handleClick= () => {
          console.log(this,1111)
          console.log(this.refs.input1.value)
        }
        handleBlur=() => {
          const {input2} = this.refs
          console.log(input2.value)
        }
    }
    ReactDOM.render(<Demo/>,document.getElementById('test1'))

第二种: 回调函数形式的ref

内联的回调函数会在state发生变化时,执行两次,怎么解决,但是并不影响开发,也经常会用到这种

class Demo extends React.Component {
      state={ isHot:false }
      render() {
        return (
          <div>
            <h2 onClick={this.handleHot}>今天的天气怎么?{this.state.isHot?'凉爽':'炎热'}</h2>
            /* 内联方式,这里的回调函数执行了几次  2次   是要state发生变化,先清空currentNode,然后在去执行第二次
通过内联形式,每次去执行时就相当于是一个新的函数,所以currentNode就是空的
*/ <input ref={currentNode => {this.input1 = currentNode;console.log('@',currentNode) } } type="text"/> <button onClick={this.handleClick}>点击</button> </div> ) } handleClick=()=>{ const {input1} = this console.log(input1.value) } handleHot=()=>{ const {isHot} = this.state this.setState({isHot:!isHot}) } } ReactDOM.render(<Demo/>,document.getElementById('test1'))

外联回调

class Demo extends React.Component {
      state={ isHot:false }
      callback = (currentNode) => {
        this.input1 = currentNode
        console.log(currentNode)
      }
      render() {
        return (
          <div>
            <h2 onClick={this.handleHot}>今天的天气怎么?{this.state.isHot?'凉爽':'炎热'}</h2>
            /* 内联方式,这里的回调函数执行了几次  2次   是要state发生变化,先清空currentNode,然后在去执行第二次
            之所以会清空currentNode是因为执行第一次是,去执行时,内联函数是一个新的函数
            */
            <input ref={ this.callback } type="text"/>
            <button onClick={this.handleClick}>点击</button>
          </div>
        )
      }
      handleClick=()=>{
        const {input1} = this
        console.log(input1.value)
      }
      handleHot=()=>{
        const {isHot} = this.state
        this.setState({isHot:!isHot})
      }
    }
    ReactDOM.render(<Demo/>,document.getElementById('test1'))

第三种:createRef的使用 (最推荐)

/* 
        react.create调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是"专人专用"的
      */
      myRef = React.createRef()
      myRef2 = React.createRef()
      showData = ()=>{
        console.log(this.myRef.current.value)
        console.log(this.myRef2.current.value)
        console.log('哈哈')
      }
      render(){
        return (
          <div>
              <input type="text" ref={this.myRef} placeholder="点击提交按钮"/> 
              <button onClick={this.showData }>点我输出左边信息</button>
              <input type="text" ref={this.myRef2} placeholder="点击提交按钮"/> 
          </div>
        )
      }
    }
    ReactDOM.render(<Demo/>,document.getElementById('test1'))