一、React初学笔记

一、React的特点

  1. 采用组件化模式、声明式编码,提高了开发效率及组件的重复率
  2. 在React Native中可以使用React语法进行移动端的开发
  3. 使用虚拟DOM+Diffing算法,尽量减少与真实DOM的交互
<body>
    <!-- 准备好一个容器 -->
    <div id="test"></div>
    <!-- react核心库 -->
    <script src="../js/react.development.js"></script>
    <!-- react扩展库 -->
    <!-- 注意必须是在核心库之后引用 -->
    <script src="../js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../js/babel.min.js"></script>
    <!-- 一定要指明type属性,否则默认是javascript -->
    <script type="text/babel">
    // 注意和JavaScript中的写法不一样,这里没有引号
        const VDOM = <h1>HelloReact</h1>
        ReactDOM.render(VDOM, document.getElementById('test'))
    </script>
</body>

二、虚拟DOM的两种创建方式

  1. 使用js的方式创建虚拟DOM
	<body>
    <!-- 准备好一个容器 -->
    <div id="test"></div>
    <!-- react核心库 -->
    <script src="../js/react.development.js"></script>
    <!-- react扩展库 -->
    <!-- 注意必须是在核心库之后引用 -->
    <script src="../js/react-dom.development.js"></script>
    <!-- 一定要指明type属性,否则默认是javascript -->
    <script>
        const VDOM = React.createElement('h1',{id:'title'}, 'helloreact')
        // 标签嵌套
        // const VDOM = React.createElement('h1',{id: 'title'}, 
        // React.createElement('span',{},'helloworld'))
        ReactDOM.render(VDOM, document.getElementById('test'))
    </script>
</body>
  1. 使用jsx的方式创建虚拟DOM
<body>
    <!-- 准备好一个容器 -->
    <div id="test"></div>
    <!-- react核心库 -->
    <script src="../js/react.development.js"></script>
    <!-- react扩展库 -->
    <!-- 注意必须是在核心库之后引用 -->
    <script src="../js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../js/babel.min.js"></script>
    <!-- 一定要指明type属性,否则默认是javascript -->
    <script type="text/babel">
    // 注意和JavaScript中的写法不一样,这里没有引号
        const VDOM = (
            <h1>
                <span>HelloReact</span>
            </h1>
               )
     //注意这里使用()可以保留格式
        ReactDOM.render(VDOM, document.getElementById('test'))
    </script>
</body>
  1. 当有多层标签嵌套时,此时可以看出使用jsx的写法比使用js的写法明显方便高效,因此建议使用jsx写法,避免使用js写法

三、虚拟DOM和真实DOM

关于虚拟DOM:

  1. 本质是Object类型的对象
  2. 虚拟DOM比真实DOM简洁,因为虚拟DOM是React内部在使用,无需真实DOM上那么多属性
  3. 虚拟DOM最终会被React转化为真实DOM,呈现在页面上

四、jsx语法规则

  1. 全称:JavaScript XML
  2. react定义的一种类似于XML的JS扩展语法:JS+XML
  3. 本质是React.createElement(component,props,…children)方法的语法糖
  4. 作用:用来简化创建虚拟DOM
  5. jsx语法规则:

    定义虚拟DOM时,不要写引号
    标签中混入JS表达式时要用{ }
    样式的类名指定要用className,而不是用class
    内联样式,要用style={{key: value}}的形式去写
    只有一个根标签
    标签必须闭合
    标签首字母
    (1)若小写字母开头,则将标签转为html中同名元素,若html中无对应的标签,则报错
    (2)若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错

五、jsx中可以的表达式

  1. { }中会自动遍历数组中的数据

  2. 注意:遍历只能是数组中的数据,对象不行

  3. js语句(代码)和js表达式:

    一个表达式会产生一个值,可以放在任何一个需要值的地方
    下面这些都是表达式:
    (1)a
    (2)a+b
    (3)fun(1)
    (4)array.map()
    (5)function test (){ } — 返回值为undefined也是有返回值
    下面这些都是语句(代码)
    (1)if(){ }
    (2)for(){ }
    (3)switch(){case: }

  4. 遍历数组数据时,每个数据都应该绑定一个唯一的key值

六、函数式组件

<body>
    <div id="test"></div>
    <script src="../js/react.development.js"></script>
    <script src="../js/react-dom.development.js"></script>
    <script src="../js/babel.min.js"></script>
    <script type="text/babel">
        function Demo() {
        	console.log(this)
        	//此处的this是undefined,因为babel编译后开启了严格模式
            return <h3>我是用函数定义的组件,适用于简单组件的定义</h3>
        }
        ReactDOM.render(<Demo/>, document.getElementById('test'))
        /*
        执行了ReactDOM.render()之后,发生了什么?
        1.React解析组件标签,找到了Demo组件
        2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面中
        */
    </script>
</body>
  • 注意:这里的组件名要大写,否则当小写的时候,会自动去html标签中进行查找

七、ES6中的类介绍

class Person {
    constructor(name, age) {
        this.name = name
        this.age = age
    }
    speak() {
        console.log(`我的名字叫${this.name},我的年纪是${this.age}`)
    }
}
const p = new Person('Tom', 18)
console.log(p);
class Student extends Person {
    constructor(name, age, sno) {
        super(name, age)
        this.sno = sno
    }
    speak() {
        console.log(`我的名字叫${this.name},我的年纪是${this.age},
        我的学号是${this.sno}`)
    }
}
const s = new Student('Tom', 18, '001')
//s.speak.call(),call函数可以改变this的指向,
//this指向call函数中的第一个参数
console.log(s);
  • 总结:

    类中的构造器不是必须写的,要对实例进行一些初始化的操作,如添加指定属性时才写
    如果A类继承了B类,且A类中写了构造器,那么A类构造器中的super是必须要调用的
    类中所定义的方法,都是放在类的原型对象上,供实例去使用

八、类式组件

  1. 创建类式组件时,必须继承React.Component类,类中可以没有构造器,但是必须要有render()函数
<body>
    <div id="test"></div>
    <script src="../js/react.development.js"></script>
    <script src="../js/react-dom.development.js"></script>
    <script src="../js/babel.min.js"></script>
    <script type="text/babel">
    //1.创建类式组件
        class MyComponent extends React.Component{
            //注意:这里的render和下面的只是同名,并不是指同一个
            render() {
                //render是放在哪里的——MyComponent的原型对象,供实例使用
                //render中的this是谁——MyComponent的实例对象<=>MyComponent组件实例对象
                console.log('render中的this:',this);
                return <h3>我是类定义组件,适用于复杂组件的定义</h3>
            }
        }
        //2.渲染组件到页面
        ReactDOM.render(<MyComponent/>, document.getElementById('test'))
        /*
        执行ReactDOM.render()之后,发生了什么?
            1、React解析组件标签,找到MyComponent组件
            2、发现组件是使用类定义的,随后new出来该类的实例,
            并通过该实例调用到原型上的render方法
            3、将render返回的虚拟DOM转化为真实的DOM,呈现在页面中
        */
    </script>
</body>

九、组件实例的三大核心属性——state

  • 当组件中有state属性时,即为复杂组件
  • state是组件对象中最重要的属性,值是对象(可以包含多个key-value的组合)
<body>
    <div id="test"></div>
    <script src="../js/react.development.js"></script>
    <script src="../js/react-dom.development.js"></script>
    <script src="../js/babel.min.js"></script>
    <script type="text/babel">
        class MyComponent extends React.Component {
            constructor(props) {
                super(props)
                this.state = {isHot: true}
            }
            render() {
                const {isHot} = this.state
                return (<h3>今天天气很{isHot ? '炎热' : '凉爽'}</h3>)
            }
        }
        ReactDOM.render(<MyComponent/>, document.getElementById('test'))
    </script>
</body>

十、事件绑定

  1. 原生的js中绑定的事件的方法有(以点击事件为例):
  • 获得DOM.addEventListener(‘click’, () => { })
  • 获得DOM.onclick = () => { }
  • 在标签内部直接监听οnclick=“fun()”
  1. 在react中推荐使用在最后一种方法,即在标签中直接监听,因为前两种方法需要操作DOM,但是在react中本身没有提供操作DOM的方法,因此应该减少直接操作DOM的方法出现
<script type="text/babel">
        class MyComponent extends React.Component {
            constructor(props) {
                super(props)
                this.state = {isHot: true}
            }
            render() {
                const {isHot} = this.state
                //注意:这里onClick使用驼峰写法
                //使用{ }
                //且定义的方法在使用时不能加()
                return (<h3 onClick={click}>今天天气很{isHot ? '炎热' : '凉爽'}</h3>)
            }
        }
        ReactDOM.render(<MyComponent/>, document.getElementById('test'))
        function click () {
            console.log('点击了标题');
        }
    </script>

十一、类中方法的this问题

class Weather extends React.Component {
     constructor(props) {
         super(props)
         this.state = {isHot: true}
     }
     render() {
         const {isHot} = this.state
         return (<h3 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'}</h3>)
     }
     changeWeather() {
     /*当试图在changeWeather函数中获得state时,
     由于在changeWeather中this的指向为undefined,获取失败
     那么为什么changeWeather中的this指向改变了呢?
     原因:
     	1、changeWeather是放在Weather的原型对象上,供实例使用
     	2、changeWeather是作为onClick的回调,所以不是通过实例调用的,是直接调用
     	3、类中的方法默认开启了局部的严格模式,所以changeWeather中的this为undefined
     	*/
         console.log(this);
     }
 }
 ReactDOM.render(<Weather/>, document.getElementById('test'))
  • 解决方法
class Weather extends React.Component {
     constructor(props) {
         super(props)
         this.state = {isHot: true}
         this.changeWeather = this.changeWeather.bind(this)
         /*
         当执行到这里时,查找到原型链上的changeWeather,
         并且将该函数的this绑定改变,返回一个名为长Weather的函数
         前一个changeWeather是实例对象上的,后一个changeWeather是原型链上的
         */
     }

十二、setState

  1. 在react中不能直接修改this.state属性值,即:使this.state.data = value,该写法是不合理的,这样的修改不能使页面中的数据随之发生改变
  2. 修改state中的属性值需要通过setState()方法
<script type="text/babel">
    class MyComponent extends React.Component {
        constructor(props) {
            super(props)
            this.state = {isHot: true}
            this.changeWeather = this.changeWeather.bind(this)
        }
        render() {
            const {isHot} = this.state
            return (<h3 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'}</h3>)
        }
        changeWeather() {
            const {isHot} = this.state
            //注意!!!!必须通过setState()方法进行修改
            this.setState({isHot: !isHot})
            console.log(isHot);
        }
    }
    ReactDOM.render(<MyComponent/>, document.getElementById('test'))
</script>

*注意

  • 当state中有多个属性时,更改其中的一个属性值,该操作是一种合并,并不是一种替换
  • 在此过程中constructor()函数调用几次:1次
    render()函数调几次:1+n次,1是初始化的那次,n是状态更新的次数
  • changeWeather()调几次:点击几次,调用几次

十三、state的简写

  1. 在前面的代码中,利用constructor()函数来解决了两个问题,此时若不写构造器,那么需要将在构造器中的两个问题解决
  2. 两个问题:
  1. state中的属性值问题:
    当在类中直接使用赋值语句时,该赋值语句即使不在构造器中,但是会直接出现在实例对象中,利用该性质,可解决第一个问题

  2. this的指向问题:
    在第一个问题的基础上,利用赋值语句的形式,在声明自定义函数时,利用赋值语句的格式,即fun = function() { }的形式定义函数,但是此时仅仅是将定义的函数挂载到了实例对象上,this的指向问题还没有解决,将定义的形式稍加修改,改为fun = () => { },即利用箭头函数的形式定义函数。箭头函数中this执行离它最近的外层中的this,指向类中的this,即类的实例对象,故达到了修改this指向问题的目的

<script type="text/babel">
     class MyComponent extends React.Component {
         state = {isHot: true}
         render() {
             const {isHot} = this.state
             return (<h3 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'}</h3>)
         }
         changeWeather = () => {
             console.log(this);
             const {isHot} = this.state
             this.setState({isHot: !isHot})
         }
     }
     ReactDOM.render(<MyComponent/>, document.getElementById('test'))
 </script>

十四、组件三大核心属性——props

  1. 关于三点运算符的一些使用方法
   let arr1 = [1, 2, 3, 4]
   //展开数组
   console.log(...arr1);
   let arr2 = [5, 6, 7, 8]
   //连接数组
   let arr3 = [...arr1, ...arr2]
   console.log(arr3);
   //作为函数的可变参数使用
   function sum(...num) {
      return num.reduce((preValue, currentValue) => {
           return preValue + currentValue
       })
   }
   console.log(sum(1,2,3,4,5));
  
   let person = {name: 'Bob', age: 18}
   /*
   console.log(...person);
   报错,该运算符不能直接展开对象,
   此写法不正确
   */
   let student = {...person}
   //实现的是深拷贝
   student.name = 'Tom'
   console.log(student);
   //{name: 'Tom', age: 18}
   console.log(person);
   //{name: 'Bob', age: 18}
   let stu = {...student, name: 'Jerry'}
   //可以在复制的时候直接修改
   console.log(stu);
   {name: 'Jerry', age: 18}
  1. props用法
<script type="text/babel">
    class MyComponent extends React.Component {
        render() {
           return (<ul>
                <li>{this.props.name}</li>
                <li>{this.props.age}</li>
            </ul>)
        }
    }
    const person = {name: '小明', age: 20}
    // ReactDOM.render(<MyComponent name="Tom" age="18"/>, document.getElementById('test'))
    ReactDOM.render(<MyComponent name="Bob" age="20"/>, document.getElementById('test'))
    //该种写法必须是person属性与标签中的属性对应才可使用
    //ReactDOM.render(<MyComponent name={person.name} age={person.age}/>, document.getElementById('test'))
    ReactDOM.render(<MyComponent {...person}/>, document.getElementById('test'))
</script>
  1. 对props进行限制
  • 在传递props的过程中,有时需要对数据作出一些限制,比如:数据类型、必传项、默认类型等
    *规定的数据类型通过propTypes来进行设置
  • 默认的数据值通过defaultProps来进行设置
<script src="../js/prop-types.js"></script>
<script type="text/babel">
    class MyComponent extends React.Component {
        render() {
           return (<ul>
                <li>{this.props.name}</li>
                <li>{this.props.age + 1}</li>
            </ul>)
        }
    }
    MyComponent.protoTypes = {
        name: PropTypes.string.isRequired,//限制name必传,且为字符串类型
        age: PropTypes.number,//限制age为number类型
        speak: PropTypes.func//限制speak为函数类型
    }
    MyComponent.defaultProps = {//设置默认值
        name: 'Tom'
    }
    //注意:这里使用age={20的写法,是保证了传值为number类型}
    ReactDOM.render(<MyComponent name="小明" age={20} />, document.getElementById('test'))
</script>
  • 注意:直接使用PropTypes.string写法时,需要引用prop-types.js文件,这是React 15.5之后的写法,在15.5前使用的是React.PropTypes.string写法(不用引入文件,15.5后弃用)
  1. props的简写:在前面的写法中,对props进行限制的代码部分是写在了类的外侧,在类的身上添加了propTypes和defaultProps,那么如何将这部分的代码该写到类的内部中呢?
class MyComponent extends React.Component {
//使用static修饰符,此时是在类上添加属性
	static protoTypes = {
      	name: PropTypes.string.isRequired,//限制name必传,且为字符串类型
      	age: PropTypes.number,//限制age为number类型
      	speak: PropTypes.func//限制speak为函数类型
   }
   static defaultProps = {//设置默认值
      name: 'Tom'
   }
    render() {
        return (<ul>
            <li>{this.props.name}</li>
            <li>{this.props.age + 1}</li>
         </ul>)
    }
  }
  1. props是只读属性
  2. 构造器与props
  • 在类中可以定义构造器,也可以不使用构造器,且在使用构造器时,通过super()函数传递了props参数,但是我们发现如果该参数不传,或者是根本就不写super()函数,此时的程序也不会报错,那么写和不写的区别到底在哪里呢?
  • 构造器是否接收props,是否传递给super,取决于:是否希望在构造器中通过this.props的方式访问props(这种用法非常少见)
  • 同时注意,通过this的方式去访问props,需要满足两个条件,一是构造器接受,二是通过super传递,若只是接收,super没有传递,则也不能通过this的方式去调用

十五、函数式组件使用props

  • 在组件实例的三大属性中,函数式组件只能使用props属性(不借助其他)
  • 将传递的数据作为props参数传递给function
<script src="../js/prop-types.js"></script>
    <script type="text/babel">
        function Person(props) {
            return (<ul>
                <li>{props.name}</li>
                <li>{props.age + 1}</li>
                <li>{props.sex}</li>
            </ul>)
        }
        //函数式组件中无法使用static
        Person.propTypes = {
            name: PropTypes.string,
            age: PropTypes.number
        }
        Person.defaultProps = {
            age: 18,
            sex: '男'
        }
        ReactDOM.render(<Person name="小明" />, document.getElementById('test'))
    </script>

十六、组件实例的三大核心属性——refs

  1. 字符串形式的ref
<script type="text/babel">
     class MyComponent extends React.Component {
         btnClick = () => {
             let {input1} = this.refs
             alert(input1.value)
         }
         render() {
             return (
                 <div>
                     <input type="text" name="" id="" ref="input1"/>
                     <button onClick={this.btnClick}>点我显示内容</button>
                 </div>
             )
         }
     }
     ReactDOM.render(<MyComponent />, document.getElementById('test'))
 //使用字符串形式的ref影响效率
 </script>
  1. 回调函数形式的ref
<script type="text/babel">
     class MyComponent extends React.Component {
         btnClick = () => {
             let {input1} = this
             console.log(this);
             // alert(input1.value)
         }
         render() {
             return (
                 <div>
                 //这里使用回调函数的形式
                 //回调函数中的形参c表示绑定ref的标签
                 //回调函数中的this指向类的实例对象,因此是将c重新赋值给类的实例对象中的属性
                 //这个属性是重新命名的input1
                 //即将整个标签的表示重新命名,并绑定到类的实例对象上
                     <input type="text" name="" id="" 
                     ref={(c) => {this.input1 = c;console.log(c);}}/>
                     <button onClick={this.btnClick}>点我显示内容</button>
                 </div>
             )
         }
     }
     ReactDOM.render(<MyComponent />, document.getElementById('test'))
 </script>

3.ref的回调次数

  • 如果ref回调函数是以内联函数的方式定义的,在更新过程(即state发生变化的时候)中它会被执行两次,第一次传入参数null,然后第二次会传入参数DOM元素。这是因为在每次渲染时会创建一个新的函数实例,所以React清空旧的ref并且设置新的。通过将ref的回调函数定义成class的绑定函数的方式可以避免上述问题,但是大多数情况下它是无关紧要的
inputContent = (c) => {
      this.input1 = c
      console.log(c);
  }
  render() {
      return (
          <div>
              <h3>今天天气{this.state.isHot ? '炎热' : '凉爽'}</h3>
              <input type="text" name="" id="" 
     //将ref的回调形式由内联形式改写成类绑定样式,就能解决
              ref={this.inputContent}/>
              <button onClick={this.btnClick}>点我显示内容</button>
              <button onClick={this.changeWeather}>点我修改天气</button>
          </div>
      )
  }
  1. createRef的使用
class MyComponent extends React.Component {
      /*
      React.createRef调用后可以返回一个容器,该容器可以存储被ref标识的节点
      且该容器是专人专用的
      */
      myRef = React.createRef()
      btnClick = () => {
          //输出结构为{current: input}
          console.log(this.myRef);
          alert(this.myRef.current.value)
      }
      render() {
          return (
              <div>
                  <input type="text" name="" id=""
                      ref={this.myRef} />
                  <button onClick={this.btnClick}>点我显示内容</button>
              </div>
          )
      }
  }
  ReactDOM.render(<MyComponent />, document.getElementById('test'))

十七、React中的事件处理

  1. 通过onXxx属性指定事件处理函数(注意大小写)
  • 这里的onXxx是自定义(合成)事件,不是原生的DOM事件——为了更好的兼容性
  • React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)——为了更高效
    如:在原生js中对于出现的ul>li标签来说,若为一个个li标签绑定事件,则过于麻烦,可以利用事件冒泡,直接给最外层的ul标签绑定事件
  1. 通过event.target得到发生事件的DOM元素对象——当要进行处理和发生的元素对象是同一个的时候,可以利用该方法减少ref的使用
  2. 官方推荐不要过度的使用ref

十八、非受控组件和受控组件

  1. 非受控组件:现用现取
<script type="text/babel">
    class Login extends React.Component {
        handle = (event) => {
            //阻止浏览器的默认刷新事件
            event.preventDefault()
            console.log(this);
            const {username, password} = this
            alert(`输入的用户名是${username.value},密码是${password.value}`)
        }
        render() {
            return (
                //默认是GET提交,参数以query形式拼接
                <form onSubmit={this.handle}>
                  用户名:<input ref={c => this.username = c} type="text" name="" id="" name="username"/>
                  密码:<input ref={c => this.password = c} type="password" name="" id="" name="password"/>  
                  <button>登录</button>
                </form>
            )
        }
    }
    ReactDOM.render(<Login/>, document.getElementById('test'))
</script>
  • 这里的input都是非受控组件,非受控组件即“现用现取”
  1. 受控组件:随着输入维护状态
    输入类的DOM,随着它的输入,将数据存储在state中,在需要的时候再在state中取出
    建议使用受控组件,减少了ref
<script type="text/babel">
     class Login extends React.Component {
         state = {
             username: '',
             password: ''
         }
      handle = (event) => {
           //阻止浏览器的默认刷新事件
           event.preventDefault()
           console.log(this);
           const {username, password} = this.state
           alert(`输入的用户名是${username},密码是${password}`)
       }
      saveUserame = (event) => {
           this.setState({username: event.target.value})
       }
      savePassword = (event) => {
          this.setState({password: event.target.value})
      }
      render() {
          return (
              //默认是GET提交,参数以query形式拼接
              <form onSubmit={this.handle}>
                用户名:<input onChange={this.saveUserame} type="text" name="" id="" name="username"/>
                密码:<input onChange={this.savePassword} type="password" name="" id="" name="password"/>  
                <button>登录</button>
              </form>
          )
      }
  }
	ReactDOM.render(<Login/>, document.getElementById('test'))
</script>

十九、高阶函数和函数的柯里化

  1. 高阶函数:

如果一个函数符合下面两个规范中的任一个,那么该函数就是高阶函数:

  • 若函数接收一个函数作为参数
  • 若函数调用的返回值是一个函数
    常见的高阶函数有:Promise、setTimeout、map、reduce等等
  1. 函数的柯里化:

通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式

  1. 利用高阶函数和函数的柯里化对前面的代码进行简化:

提出疑问?
在前面的程序中,利用受控组件保存表单数据时,saveUserame和savePassword的处理部分代码相似,冗余部分太多,当表单中需要进行处理的数据越来越多时,这部分的冗余代码会随之增加,那么有什么方法能利用一个函数对这些进行统一的判断处理呢?
在绑定onChange函数时,给回调函数传入一个参数,利用该参数分辨此时进行处理的是哪个数据,但是在前面的学习中,若给onChange = {this.fun()}加上(),会直接执行该函数,将函数的返回结果直接传给onChange,而不是实现当需要时才调用
这里就利用到了高阶函数和函数的柯里化来解决这个问题

<script type="text/babel">
    class Login extends React.Component {
        state = {
            username: '',
            password: ''
        }
        //#region
          /*
          该注释部分可以折叠
          */
         //#enderegion
     //利用高阶函数
     handle = (event) => {
          event.preventDefault()
          console.log(this);
          const {username, password} = this.state
          alert(`输入的用户名是${username},密码是${password}`)
      }
      //函数的柯里化
      saveInfo = (dataType) => {
          return (event) => {
              this.setState({[dataType]: event.target.value})
          }
      }
     render() {
         return (
             <form onSubmit={this.handle}>
               用户名:<input onChange={this.saveInfo('username')} type="text" name="" id="" name="username"/>
               密码:<input onChange={this.saveInfo('password')} type="password" name="" id="" name="password"/>  
               <button>登录</button>
             </form>
         )
     }
 }
    ReactDOM.render(<Login/>, document.getElementById('test'))
</script>

二十、不使用柯里化的写法

  • 不使用柯里化的写法,该怎样将dataType和event同时传递给saveInfo函数呢?——直接使用回调函数处理
<script type="text/babel">
    class Login extends React.Component {
        state = {
            username: '',
            password: ''
        }
     handle = (event) => {
          event.preventDefault()
          console.log(this);
          const {username, password} = this.state
          alert(`输入的用户名是${username},密码是${password}`)
      }
      saveInfo = (dataType, event) => {
          this.setState({[dataType]: event.target.value})
          }
      }
     render() {
         return (
             <form onSubmit={this.handle}>
               用户名:<input onChange={event => this.saveInfo('usename', event)} type="text" name="" id="" name="username"/>
               密码:<input onChange={event => this.saveInfo('password', event)} type="password" name="" id="" name="password"/>  
               <button>登录</button>
             </form>
         )
     }
 }
    ReactDOM.render(<Login/>, document.getElementById('test'))
</script>

二十一、生命周期函数(钩子函数)(旧)

生命周期函数图(旧)

  1. 初始化阶段:
  • 由ReactDOM.render()触发——初次渲染
    1.constructor()
    2 componentWillMount()
    3 render()
    4 componentDidMount()——常用:一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求
  1. 更新阶段(分别有三种情况):

1.第一种:通过setSate函数来更新状态,此时默认会触发render函数,在1线路中,shouldComponentUpdate函数相当于一个“开关阀”,用来控制是否允许更新状态时,调用render函数,该函数要求一定要有一个返回值,默认为true,但是当我们对该函数进行重写时,以重写的函数为主。若重写的返回值为false,则“开关阀”关闭,该线路将不再进行下去
2. 第二种:通过foreUpdate函数在状态不更新的时候强制调用了render函数,达到了不更新状态但是能重新渲染页面的效果
3. 第三种:线路线是在父子组件中的情况,父组件将自身的props传递给子组件,函数componentWillReceiveProps接收参数props,即父组件中传来的props对象,子组件通过自己的setState方法更新,此时触发的是线路3

  1. 卸载组件:
  • 由ReactDOM.unmountComponentAtNode()触发
  • componentWillUnmount()——常用:一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
  1. 重要的钩子:
  1. render:初始化渲染或更新渲染调用
  2. componentDidMount:开启监听,发送ajax请求
  3. componentWillUnmount:做一些收尾工作,如:清理定时器
  1. 即将废弃的钩子:
  1. componentWillMount
  2. componentWillReceiveProps
  3. componentWillUpdate
    现在使用会出现警告,下一个版本需要加上UNSAFE_前缀才能使用,以后可能会被彻底废弃,不建议使用
//线路3,当父组件中更新state时执行
class A extends React.Component {
     state = {isHot: true}
     changeWeather = () => {
         let {isHot} = this.state
         this.setState({isHot: !isHot})
     }
     render() {
         return (
             <div>
                 <h3>今天天气{this.state.isHot ? '炎热':'凉爽'}</h3>
                 <button onClick={this.changeWeather}>修改天气</button>
                <B name="Bob"/> 
             </div>
         )
     }
 }
 class B extends React.Component {
     componentWillReceiveProps(props){
         console.log('B组件',props);
     }
     shouldComponentUpdate() {
         console.log('B中的shouldComponentUpdate');
         return false
     }
     render() {
         console.log('B中的render');
         return (
             <div>
                 我是B中的内容
             </div>
         )
     }
 }
 ReactDOM.render(<A name="Tom" age={18}/>, document.getElementById('test'))

二十二、生命周期函数(新)

  • 注意:要使用新的钩子函数,需要引入新的包
    React生命周期函数(新)
  1. getDerivedStateFromProps函数的使用:
  • 该函数使用时,需加static
  • 该函数需要一个返回值,任何值都可
  • 该函数接收两个参数,props和state
  • 该函数是在当state的值任何时刻都取决于props时使用
  • state被props影响的情况:
    当return一个对象时,且该对象的属性名在state中也有,那么state中的值会以这个对象中的值为准,且不能被改变了
    当组件中被传递了props且直接return props,那么会以传递的为准,但是需要注意的是:当传递的props中的name在state中存在,则替换,不存在则将props加入到state中
class Test extends React.Component{
     state = {data: '今天天气很好'}
     static getDerivedStateFromProps (props, state){
         console.log('getDerivedStateFromProps',props,state);
         //state中有data,更新
         // return {data: '今天天气不好'}
         return props
     }
     render() {
         return (
             <div>
                 <h3>{this.state.data}</h3>
             </div>
         )
     }
 }
 ReactDOM.render(<Test data="Tom"/>, document.getElementById('test'))
 //属性为name,state中是data,则将{name: 'Tom'}添加到state中
 // ReactDOM.render(<Test name="Tom"/>, document.getElementById('test'))
  1. getSnapshotBeforeUpdate函数的使用:
  • 和componentDidUpdate函数结合使用,getSnapshotBeforeUpdate函数是在组件更新的前一刻,将某个值传递给componentDidUpdate
  • componentDidUpdate函数接收三个参数
    componentDidUpdate(preProps,preState,snapshotValue)
    这里的snapshotValue就是getSnapshotBeforeUpdate函数最后的返回值
  1. 案例:页面持续更新“新闻”,但是停留位置不随着滚动条的滚动而滚动
    效果图:
    在这里插入图片描述
class Test extends React.Component {
      state = { newArr: [] }
      componentDidMount() {
          setInterval(() => {
              const {newArr} = this.state
              const news = '新闻' + (newArr.length+1)
              this.setState({newArr: [news, ...newArr]})
          }, 1000)
      }
      getSnapshotBeforeUpdate() {
          return this.refs.list.scrollHeight
      }
      componentDidUpdate(preProps,preState,snapshotValue) {
      //this.refs.list.scrollHeight - snapshotValue
      //计算出每个新闻的高度
          this.refs.list.scrollTop += this.refs.list.scrollHeight - snapshotValue
      }
      render() {
          return (
              <div className="box" ref="list">
                  {
                      this.state.newArr.map((item, index) => {
                          return <div key={index} className="content">{item}</div>
                      })
                  }
              </div>
          )
      }
  }
  ReactDOM.render(<Test/>, document.getElementById('test'))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值