react笔记-基础

文章目录

初识

react是一个将数据渲染为HTML视图的开源JavaScript库

React的优点

  1. 采用组件化模式,声明式编码,提高开发效率及组件复用率
  2. 在React Native中可以使用React语法进行移动端开发(React Native可以编写苹果安卓应用)
  3. 使用虚拟dom+优秀的Diffing算法,尽量减少与真实DOM的交互,最小化页面重绘

Hello React

babel.js
用来转换js和jsx
可以通过 CDN 获得 React 和 ReactDOM 的 UMD 版本。

引入顺序要主意
引入核心库
dom
babel

<!-- 准白好一个容器 -->
<div id="test"></div>

<!-- 引入核心库 -->
<script src="../react.development.js"></script>
<!-- 引入react-dom用于支持react操作DOM -->
<script src="../react-dom.development.js"></script>
<script src="../babel.min.js"></script>
<script type="text/babel"> //此处必须写成Babel
  //创建虚拟dom
  const Virtual = (  //此处一定不要写引号 因为是虚拟dom
    <h1>hello,react</h1>
  )
  //渲染虚拟dom到页面
  // ReactDOM.render(虚拟DOM,容器)
  //ReactDOM.render(Virtual,"#test") //react没有提供这样的写法
  ReactDOM.render(Virtual,document.getElementById('test'));
</script>

js和jsx 区别

使用js来创建虚拟dom
  const VDOM = React.createElement("h1, {id:"title"}, React.createElement("span"));
  
  
使用jsx 会自动转换成上面的样子 其实就是语法糖
  const Virtual = ( 
    <h1>
		<span id="title"></span>
    </h1>
  )

虚拟dom 注释

  const Virtual = ( 
    <h1>
		{ /* <span id="title"></span> */ } 
		{变为代码块}  {/* 代码块里就放个注释 */}
    </h1>
  )


  const Virtual = ( 
    <h1>
		<span id="title"></span>
    </h1>
  )
  
  
  虚拟dom Virtual就是一个对象
  
  debuger测试看来说
  虚拟dom比较轻 因为很多用不上

jsx语法规则javascript xml

jsx本质是语法糖const VDOM = React.createElement("h1, {id:“title”},React.createElement(“span”));

标签混入js表达式时要用{} -----何为表达式?何为语句?

  const myId = 'aTguiGu'
  const myData = 'hello,ReaCt'
  const Virtual = (
    <h1 id={myId.toLocaleLowerCase()}> //标签混入js表达式时要用{}
      <span>{myData.toLocaleLowerCase()}</span>  
    </h1>
  )
  ReactDOM.render(Virtual,document.getElementById('test'));
  
  
  表达式
  a
  a+b
  demo(1)
  arr.map()
  function(){}
  
  const i = ?能接到值就是表达式
  
  
  语句
  if(){}
  for(){}
  switch(){}
  
  const i = 接不到值

jsx中样式class要写className 因为和js中class类冲突


  const Virtual = (  //此处一定不要写引号 因为是虚拟dom
    <h1 className="title" id={myId.toLocaleLowerCase()}>
      <span>{myData.toLocaleLowerCase()}</span>  
    </h1>
  )

内联样式需要双括号 小驼峰 sytle={{key:value,key:value}}

  const Virtual = (  //此处一定不要写引号 因为是虚拟dom
    <h1 className="title" id={myId.toLocaleLowerCase()}>
      <span style={{color:'red',fontSize:'50px'}}>{myData.toLocaleLowerCase()}</span>  
    </h1>
  )

只能有一个根标签 标签必须闭合

标签首字母

若小写字母开头,则将标签转为html中同名元素,若html没有对应元素 则报错
若大写字母开头,直接找对应组件

小练习 渲染数组的值 react会自动遍历数组

react会自动帮你遍历数组

  const data = ['Angular','React','Vue']
  const Virtual = (
    <div>
      <h1>前端框架</h1>
      <ul>
        {
          data.map((item,index)=>{ //只能写表达式
            return <li key={index}>{item}</li>
          })
        }
      </ul>  
    </div>
  )
  ReactDOM.render(Virtual,document.getElementById('test'));

面向组件编程

谷歌网上商店下载安装react dev tools

控制台就可以看组件和测试工具

定义组件的方法

函数式组件

函数必须有返回值
挂在必须为结束标签

  // 创建函数式组件
  function MyComponent() {
    return <h2>我是函数定义组件(适用于【简单组件】的定义)</h2>  //必须有返回值
  }
  					//必须为结束标签
  ReactDOM.render(<MyComponent/>,document.getElementById('test'));

class类的使用

//创造一个Person类
class Parson{
  //构造器
  constructor(name,age){
    this.name = name
    this.age = age
  }
  //一般方法
  speak(){
    //speak写在原形链上面
    console.log(`我叫${this.name},我今年${this.age}`);
  }
}


//创建一个学生类 继承parson
class Student extends Parson{
  //没有构造器 直接搬过来继承的构造器
}

const s1 = new Student("小张",12)
console.log(s1);

如果继承以后有新的属性要用构造器

//创造一个Person类
class Parson{
  //构造器
  constructor(name,age){
    this.name = name
    this.age = age
  }
  //一般方法
  speak(){
    //speak写在原形链上面
    console.log(`我叫${this.name},我今年${this.age}岁`);
  }
}


//创建一个学生类 继承parson
class Student extends Parson{
  constructor(name,age,grade){
    super(name,age)   //super只能写到第一行
    this.grade = grade
  }
}

const s1 = new Student("小张",12,"高一")  //比parson多了一个年级
console.log(s1);

类中的构造器不是必须的 不写也可以
类中的方法都放到了原型对象上

类式组件

类必须继承React内置类
必须有render方法
render必须有返回值



  // 创建类式组件
  class MyComponent extends React.Component{  //必须继承
    render(){  //render方法
      return <h2>我是类定义组件(适用于【复杂组件】的定义)</h2>  //返回值
    }
  }
  //渲染组件到页面
  ReactDOM.render(<MyComponent/>,document.getElementById('test'))
  
  /*
  class MyComponent类里面的render是供实例使用
  但是并没有new一个实例
  当渲染的时候 react会自动new一个实例 并调用render方法
  */
  
  
  
  // 创建类式组件
  class MyComponent extends React.Component{
    render(){
      // render放在那?--MyComponent的原形对象上,供实例使用
      // rander中的this是谁?--MyComponent的实例对象 或组件实例对象
      console.log(this);
      return <h2>我是类定义组件(适用于【复杂组件】的定义)</h2>
    }
  }
  //渲染组件到页面
  ReactDOM.render(<MyComponent/>,document.getElementById('test'))

$state-组件实例的核心属性- 对state(状态)的理解

函数式组件=简单组件=无state
类式组件=复杂组件=有state

解决changeWeather中this指向问题

class Weather extends React.Component{
  constructor(props){
    super(props)
    //初始化状态
    this.state = {isHot:true}
  }
  render(){
    //读取状态
    const {isHot} = this.state
    		//这里必须写this.changeWeather因为直接写拿不到
    return <h1 onClick={this.changeWeather.bind(this)}>今天天气很{isHot?"炎热":"寒冷"}</h1>
  }
  changeWeather(){
  	//局部会自动开启严格模式 所以上面不写bind的话this指向undefined
    console.log(this.state);
  }
}
ReactDOM.render(<Weather/>,document.getElementById("test"))

或者以下
构造器里的this就是实例对象

class Weather extends React.Component{
  constructor(props){
    super(props)
    //初始化状态
    this.state = {isHot:true}
    this.changeWeather = this.changeWeather.bind(this) //构造器里的this就是实例对象  这里挂上 点击输出的就是自身的changeWeather
  }
  render(){
    //读取状态
    const {isHot} = this.state
    return <h1 onClick={this.changeWeather}>今天天气很{isHot?"炎热":"寒冷"}</h1>
  }
  changeWeather(){
    console.log(this.state);
  }
}
ReactDOM.render(<Weather/>,document.getElementById("test"))

setState的使用 类似小程序

通过上面解决了指向问题 现在要点击变换true和false

class Weather extends React.Component{
  constructor(props){
    super(props)
    //初始化状态
    this.state = {isHot:true}
    this.changeWeather = this.changeWeather.bind(this)
  }
  render(){
    //读取状态
    const {isHot} = this.state
    return <h1 onClick={this.changeWeather}>今天天气很{isHot?"炎热":"寒冷"}</h1>
  }
  changeWeather(){
  	//这里确实改了 但是页面不会炎热寒冷一直切换
    this.state.isHot = !this.state.isHot
    console.log(this.state.isHot);
  }
}
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)
  }
  render(){
    //读取状态
    const {isHot} = this.state
    return <h1 onClick={this.changeWeather}>今天天气很{isHot?"炎热":"寒冷"}</h1>
  }
  changeWeather(){
    //严重注意state状态不可直接更改
    // this.state.isHot = !this.state.isHot //错误写法
    // console.log(this.state.isHot);

    //由于上面写了this.changeWeather = this.changeWeather.bind(this)
    //则这里面的this就是Weather的实例对象
    //实例对象的原型上面有render 有changeWeather 原型的原型是React.Component 里面有个方法叫setState
    this.setState({
      isHot:!this.state.isHot
    })
  }
}
ReactDOM.render(<Weather/>,document.getElementById("test"))

整体结构的小总结 精简方式

小总结

class Weather extends React.Component{
  constructor(props){   //只会调用一次 生成实例的时候
    super(props)
  }
  render(){} //1+n次 先调用把组件return出去 然后当事件函数先触发修改状态 重新渲染则在调用
  changeWeather(){} //触发几次执行几次
}
ReactDOM.render(<Weather/>,document.getElementById("test"))





class Weather extends React.Component{
  constructor(props){  //构造器为啥要写?
    super(props)
    //必须初始化状态
    this.state = {
      isHot:true,
      wind:"微风"
    }
    //解决this指向问题
    this.changeWeather = this.changeWeather.bind(this)
  }
  render(){ //render里的this就是组件实例对象
    //读取状态
    const {isHot,wind} = this.state
    //做展示
    return <h1 onClick={this.changeWeather}>今天天气很{isHot?"炎热":"寒冷"}</h1>
  }
  changeWeather(){
    this.setState({
      isHot:!this.state.isHot //获取值 更新值
    })
  }
}
ReactDOM.render(<Weather/>,document.getElementById("test"))

精简state

class Weather extends React.Component{
  constructor(props){}  //this肯定指向实例对象
  render(){}		//react内部new出来实例对象 会自动调用render所以this也是实例对象
  changeWeather(){} // this是undefined 内部自动严格模式
}

constructor里面只有两件事 解决this指向 初始化状态
类里面可以直接写赋值语句 但是必须死值
class Weather extends React.Component{
  constructor(props){}
  render(){}
  changeWeather(){}
  a = 1
}
new出来的实例就会有a=1的属性 等价于在constructor里面写了
this.a = 1

所以直接精简state
class Weather extends React.Component{
  constructor(props){}
  state = {name:"xxx"}
  render(){}
  changeWeather(){}
}

精简constructord的this指向问题

class Weather extends React.Component{
  constructor(props){}
  render(){}
  changeWeather = function(){} //这样写就是赋值语句 和a=1一个意思
  //本来这个方法是在原型上 这样就直接到了实例的属性上
  //光这样写还是不行 仅仅是换了个地方 原型到自身而已
}


class Weather extends React.Component{
  constructor(props){}
  render(){}
  //箭头会拿外部的this
  log(this)?? //类里面不能写函数体 所以不必纠结 既然箭头拿外部this 直接箭头里输出this
  changeWeather = ()=>{
  	console.log(this) //为weather的实例对象
  }
}

最终结构 注意要点

class Weather extends React.Component{
  //初始化状态
  state = {
    isHot:true,
    wind:"寒冷"
  }
  
  render(){
    const {isHot,wind} = this.state
    return <h1 onClick={this.changeWeather}>今天天气很{isHot?"炎热":"寒冷"},{wind}</h1>
  }
  
  //自定义方法--赋值语句+箭头函数
  changeWeather = ()=>{
    this.setState({
      isHot:!this.state.isHot
    })
  }
}
ReactDOM.render(<Weather/>,document.getElementById("test"))

注意要点

  1. 组件中render的this为组件实例对象 (因为react自动new并调用)
  2. 组件自定义方法中this为undefined(因为类中自动为严格模式) 并且事件调用时没有this
    a. 强制绑定this 通过bind() (bind不同于call不会执行)
    b.通过箭头函数
  3. 状态数据不能直接修改 必须拿到实例原型的原型React里面的setState来改

$props-组件实例的核心属性- props

基本使用 简单传值

//创建组件
class Parson extends React.Component{
  state = { name:'tom', age:18, sex:'女' }
  render(){
    const {name,age,sex} = this.state
    return (
      <ul>
        <li>姓名:{name}</li>
        <li>性别:{age}</li>
        <li>年龄:{sex}</li>
      </ul>
    )
  }
}
ReactDOM.render(<Parson/>,document.getElementById('app1'))
ReactDOM.render(<Parson/>,document.getElementById('app2'))
ReactDOM.render(<Parson/>,document.getElementById('app3'))

这是基本写法,但是现在 我需要从外部拿到数据 来渲染 上面这种写法像是自家的事情
而且渲染的三个人都是一个名字性别年龄

//创建组件
class Parson extends React.Component{
  render(){
    console.log(this);
    const {name,age,sex} = this.props
    return (
      <ul>
        <li>姓名:{name}</li>
        <li>性别:{age}</li>
        <li>年龄:{sex}</li>
      </ul>
    )
  }
}
ReactDOM.render(<Parson name='tom' age='18' sex='女' />,document.getElementById('app1'))
			//这里react会自动把name:tom传到props里面

批量传递props 比如信息很多

批量传递props
批量传递标签属性

    //创建组件
    class Parson extends React.Component {
      render() {
        console.log(this);
        const { name, age, sex } = this.props
        return (
          <ul>
            <li>姓名:{name}</li>
            <li>性别:{age}</li>
            <li>年龄:{sex}</li>
          </ul>
        )
      }
    }
    const result = { name: "老刘", age: 18, sex: "女" }
    ReactDOM.render(<Parson name={result.name} age={result.age} sex={result.sex} />, document.getElementById('app1'))
    //nonono如果数据太多 则不能这样写
    					//这样写
    ReactDOM.render(<Parson {...result} />, document.getElementById('app2'))
    
    
    
    
    
测试。。。
var a = {n: 1, b: 2, c: "你好"}
var b = {say:"hello"}
var obj = {...b,...a} 或者 var obj = new Object({...b,...a})

obj的值为{say: "hello", n: 1, b: 2, c: "你好"}

对props进行限制propTypes

限制必须写到类上面 而不是实例上面

    //创建组件
    class Parson extends React.Component {
      render() {
        console.log(this);
        const { name, age, sex } = this.props
        return (
          <ul>
            <li>姓名:{name}</li>
            <li>性别:{age}</li>
            <li>年龄:{sex}</li>
          </ul>
        )
      }
    }
    //固定写法 react每次new都会去问Parson类身上有没有这个东西
    Parson.propTypes = {
      //开头大写的P,这是react的内置属性
      name:React.propTypes.string  //类型也为小写
    }
    const result = { name: "老刘", age: 18, sex: "女" }
    ReactDOM.render(<Parson name={result.name} age={result.age} sex={result.sex} />, document.getElementById('app1'))
    ReactDOM.render(<Parson {...result} />, document.getElementById('app2'))
    

限制规则

    Parson.propTypes = {
      //开头大写的P,这是react的内置属性
      name:React.propTypes.string  //类型也为小写
    }
    但是这种写法在16.xx后被弃用 因为react上带着propTypes会变的很大 
    而且有时候也不一定非要进行限制
    
    
    16.xx以后 就是新弄一个一个依赖包来进行 需要就载入prop-types.js
	用来对组件标签进行限制 引入则多了一个对象 PropTypes
	直接在类.PropTypes里写
	    //固定写法 react每次new都会去问Parson类身上有没有这个东西
    Parson.propTypes = {
      //开头大写的P,这是react的内置属性
      name:PropTypes.string.isRequired,  //类型也为小写 必须填写
      sex:propTypes.string,
    }

默认值

    //指定默认值
    Parson.defaultProps = {
      sex:"不男不女",
      age:18
    }
    
    

如果穿的是方法 则限制改为handel:PropTypes.func

    //创建组件
    class Parson extends React.Component {
      render() {
        const { name, age, sex,handel } = this.props
        return (
          <ul>
            <li>姓名:{name}</li>
            <li>性别:{age}</li>
            <li>年龄:{sex}</li>
          </ul>
        )
      }
    }
    //固定写法 react每次new都会去问Parson类身上有没有这个东西
    Parson.propTypes = {
      //开头大写的P,这是react的内置属性
      name:PropTypes.string.isRequired,  //类型也为小写 必须填写
      sex:PropTypes.string,
      age:PropTypes.number,
      handel:PropTypes.func //传方法
    }
    //指定默认值
    Parson.defaultProps = {
      sex:"不男不女",
      age:18,
    }
    function handle(){}
    const result = { name: "老刘",sex:"男", age: 18,handle:handle}
    ReactDOM.render(<Parson {...result} />, document.getElementById('app2'))

propTypes的简写方式 props只读不能改

props是只读的不能this.props等于xxx

      render() {
        const { name, age, sex,handel } = this.props
        this.prop.xxx = xxx //报错
        return (
          <ul>
            <li>姓名:{name}</li>
            <li>性别:{age}</li>
            <li>年龄:{sex}</li>
          </ul>
        )
      }

简写

    class Parson extends React.Component {
      render() {
        const { name } = this.props
        return (
          <ul>
            <li>姓名:{name}</li>
            <li>性别:{age}</li>
            <li>年龄:{sex}</li>
          </ul>
        )
      }
      static propTypes = { //直接写进去是写到了实例对象上 并不是类上面 必须在前面加static静态的
        name: PropTypes.string.isRequired
      }
      static defaultProps = {
        sex: "不男不女"
      }
    }
    
    Parson.propType = {  //如果把这个直接写进去 得加static
        name: PropTypes.string.isRequired,
    }
    
    
    
    
    
类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。

class Foo {
  static say() {
    return 'hello';
  }
}
Foo.say() // 'hello'

var foo = new Foo();
foo.say() // TypeError: foo.classMethod is not a function

注意,如果静态方法包含this关键字,这个this指的是类,而不是实例。

类式组件中的构造器与props

在 React 组件挂载之前,会调用它的构造函数。在为 React.Component 子类实现构造函数时,应在其他语句之前前调用 super(props)。否则,this.props 在构造函数中可能会出现未定义的 bug。


      constructor(props){
        super() //这里不传的话
        console.log(props);
        console.log(this.props); //那么这里就有可能拿不到 直接为undefined
      }

函数式组件的props

函数因为可以拿参数所以可以有props
state只能后面学到hooks
refs不可以

    function Parson(props) {
      const { name, sex, age, } = props
      return (
        <ul>
          <li>姓名:{name}</li>
          <li>性别:{age}</li>
          <li>年龄:{sex}</li>
        </ul>
      )
    }
    Parson.propTypes = {
      name: PropTypes.string.isRequired,
      sex: PropTypes.string,
      age: PropTypes.number,
      handel: PropTypes.func
    }
    Parson.defaultProps = {
      sex: "不男不女",
      age: 18,
    }
    function handle() { }
    const result = { name: "老刘", age: 18, handle: handle }



    ReactDOM.render(<Parson {...result} />, document.getElementById('app1'))

props总结

//组件内部不要修改props的值 为只读


//可以使用{...obj}来批量传递
ReactDOM.render(<Parson {...result} />, document.getElementById('app1'))


//进行props限制
类名.defaultProps = {
      sex: "不男不女",
      age: 18,
    }
或者写在类里面加static


//函数式组件也可以用props

$refs-组件实例的核心属性 与 事件处理

字符串形式的ref-已不被推荐使用16.8还能用

首先有一个需求
两个input框
第一个输入内容点击按钮则弹出显示内容
第二个输入内容失去焦点则弹出显示内容


		//基本写法
    class Parson extends React.Component {
      render() {
        return (
          <div>		//这里加了id
            <input id="i1" type="text" placeholder="点击按钮输出数据" />
            <button onClick={this.btn}>点我弹出左侧数据</button>
            <input type="text" placeholder="失去焦点输出数据" />
          </div>
        )
      }
      btn = () => {
        const i = document.getElementById("i1")  //可是都用了react没必要这样写
        alert(i.value)
      }
    }
    ReactDOM.render(<Parson />, document.getElementById('app1'))
    
    
    
    
   		//使用react里的ref
    class Parson extends React.Component {
      render() {
        return (
          <div>		//使用ref
            <input ref="input1" type="text" placeholder="点击按钮输出数据" />
            <button onClick={this.btn1}>点我弹出左侧数据</button>
            <input ref="input2" onBlur={this.btn2} type="text" placeholder="失去焦点输出数据" />
          </div>
        )
      }
      btn1 = () => {
        console.log(this.refs.input1.value) //这里用refs
      }
      btn2 = () => {
        console.log(this.refs.input2.value)
      }
    }
    ReactDOM.render(<Parson />, document.getElementById('app1'))

回调形式的ref 调用次数的小问题

标签必须用ref关键字

    class Parson extends React.Component {
      render() {
        return (
          <div>  //往外找this 找到render 找到实例
            <input ref={c => this.input1 = c} type="text" placeholder="点击按钮输出数据" />
            <button onClick={this.btn1}>点我弹出左侧数据</button>
            <input ref={(c) => {this.input2 = c}} onBlur={this.btn2} type="text" placeholder="失去焦点输出数据" />
          </div>
        )
      }
      btn1 = () => {
      	console.log(this.input1.value);
      }
      btn2 = () => {
      	console.log(this.input2.value);
      }
    }
    ReactDOM.render(<Parson />, document.getElementById('app1'))

回调形式直接写回调函数 this指向recder指向实例

调用次数的小问题
回调函数调用次数 问题 当写为回调函数 且状态被修改后 react会再次调用render 回调函数会重置为null在赋值为你写的函数

当代码如下时

    class Parson extends React.Component {
      state = {
        isHot: true
      }
      render() {
        const { isHot } = this
        return (
          <div>
            <input ref={c => { this.input1 = c; console.log("输出了", c); }} type="text" />
            <button onClick={this.btn1}>点我弹出左侧数据</button>
            <h1 onClick={this.handleH1}>今天天气{isHot ? "很热" : "寒冷"}</h1>
          </div>
        )
      }
      btn1 = () => {
        console.log(this.input1.value);
      }
      handleH1 = () => {
        this.setState({
          isHot:!this.isHot
        });
      }
    }
    ReactDOM.render(<Parson />, document.getElementById('app1'))

log会在页面生成的时候输出一句输出了
因为react默认执行一次render

当点击和标签时
输出结果如下:
输出了 null
Inline Babel script:11 输出了
因为当状态改变reder重新运行 会初始化为null 在运行函数

解决方案
挂载到自身的函数中 class中的绑定函数
就可以避免 但是大多数情况下 是无关紧要的

    class Parson extends React.Component {
      state = {
        isHot: true
      }
      render() {
        const { isHot } = this
        return (
          <div>
            <input ref={this.saveInput} type="text" />
            <button onClick={this.btn1}>点我弹出左侧数据</button>
            <h1 onClick={this.handleH1}>今天天气{this.state.isHot ? "很热" : "寒冷"}</h1>
          </div>
        )
      }
      saveInput = (c)=>{
        this.input1 = c
        console.log("输出",c);
      }
      btn1 = () => {
        console.log(this.input1.value);
      }
      handleH1 = () => {
        this.setState({
          isHot:!this.state.isHot
        });
      }
    }
    ReactDOM.render(<Parson />, document.getElementById('app1'))

createRef的使用

    class Parson extends React.Component {
      myRef1 = React.createRef() //调用后可以返回一个容器 该容器可以存储被ref标识的节点
      //等于我弄了个容器挂在的实例自身 起名为myRef
      //但是该容器 专人专用 里面只能存一个
      myRef2 = React.createRef()
      state = {
        isHot: true
      }
      render() {
        const { isHot } = this
        return (
          <div>
            <input ref={this.myRef1} type="text" />
            {/*react发现你放的是React.createRef()创造的容器 就把当前的节点存到容器里*/}
            <input ref={this.myRef2} onBlur={this.handle2} type="text" />
            <button onClick={this.handle1}>点我弹出左侧数据</button>
          </div>
        )
      }
      handle1 = () => {
        console.log(this.myRef1.current); //这里的current是react给你生成的
      }
      handle2 = () => {
        console.log(this.myRef2.current);
      }
    }
    ReactDOM.render(<Parson />, document.getElementById('app1'))

例子2

    class Parson extends React.Component {

      myRef = {
        myRef1:React.createRef(),
        myRef2:React.createRef()
      }
      state = {
        isHot: true
      }
      
      render() {
        const { isHot } = this.state

        //这里尝试结构结果不行
        //只能一个一个
        const {myRef1,myRef2} = this.myRef
        return (
          <div>
            <input ref={myRef1} type="text" />
            {/*react发现你放的是React.createRef()创造的容器 就把当前的节点存到容器里*/}
            <input ref={myRef2} onBlur={this.handle2} type="text" />
            <button onClick={this.handle1}>点我弹出左侧数据</button>
          </div>
        )
      }
      handle1 = () => {
        console.log(this.myRef.myRef1.current); //这里的current是react给你生成的
      }
      handle2 = () => {
        console.log(this.myRef.myRef2.current);
      }
    }
    ReactDOM.render(<Parson />, document.getElementById('app1'))

事件处理 可以用target的时候少用refs

  1. 通过onXxx属性指定事件处理函数(注意大小写)
    a. React使用的是自定义(合成)事件,而不是使用的元素DOM事件 – 为了更好的兼容性封装过
    b. React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) – 为了高效
  2. 通过event.target得到发生事件的DOM元素对象

收集表单数据

受控 非受控组件

受控组件
现用现取
输入类的dom随着你的输入维护到state里 需要的时候直接从状态里取

    class Login extends React.Component {

      state = {
        username:'',
        password:''
      }
      render() {
        return (
          <div>
            <form action="" onSubmit={this.handleSubmit}>
              用户名<input onChange={this.saveUsername} type="text" name="username" />
              密码<input onChange={this.savePassword} type="password" name="password" />
              <button>登陆</button>
            </form>
          </div>
        )
      }
      saveUsername = (e) => {
        this.setState({
          username:e.target.value
        })
      }
      savePassword = (e) => {
        this.setState({
          password:e.target.value
        })
      }
      handleSubmit = (e) => {
      	//这里打印完会转跳到form里面的action的地址
        e.preventDefault() //阻止默认事件 阻止表单提交
        const {username,password} = this.state
        console.log(username,password);
      }
    }
    ReactDOM.render(<Login />, document.getElementById('app1'))

非受控
用ref取到节点当前的值

很多的使用柯里化来写

如果我想实现登陆可以这样写

    class Login extends React.Component {

      state = {
        username:'',
        password:''
      }
      render() {
        return (
          <div>
            <form action="" onSubmit={this.handleSubmit}>
              用户名<input onChange={this.saveUsername} type="text" name="username" />
              密码<input onChange={this.savePassword} type="password" name="password" />
              <button>登陆</button>
            </form>
          </div>
        )
      }
      saveUsername = (e) => {
        this.setState({
          username:e.target.value
        })
      }
      savePassword = (e) => {
        this.setState({
          password:e.target.value
        })
      }
      handleSubmit = (e) => {
        e.preventDefault() //阻止默认事件 阻止表单提交
        const {username,password} = this.state
        console.log(username,password);
      }
    }
    ReactDOM.render(<Login />, document.getElementById('app1'))

但是如果我要注册 手机很多的信息 这样的写法就太臃肿了

第一种方法 事件触发时传进去
    class Login extends React.Component {

      state = {
        username: '',
        password: ''
      }
      render() {
        return (
          <div>
            <form action="" onSubmit={this.handleSubmit}>
              用户名<input onChange={(e)=>this.saveFormData('username',e)} type="text" name="username" />
              密码<input onChange={(e)=>this.saveFormData('password',e)} type="password" name="password" />
              <button>登陆</button>
            </form>
          </div>
        )
      }
      //保存表单数据到state中
      saveFormData = (v,e) => {
        console.log(v,e.target.value);
        this.setState({
          [v]:e.target.value //这里要加[]要不然会当你写的是'v' 字符串 不会读变量数据
        })
      }
    }
    ReactDOM.render(<Login />, document.getElementById('app1'))
    
    
    -------------------------------------------------------------------
    //第二种刚开始就执行传进去username 函数柯里化
    class Login extends React.Component {

      state = {
        username: '',
        password: ''
      }
      render() {
        return (
          <div>
            <form action="" onSubmit={this.handleSubmit}>
            					//页面刚开始就会执行 把username传过去
              用户名<input onChange={this.saveFormData('username')} type="text" name="username" />
              密码<input onChange={this.saveFormData('password')} type="password" name="password" />
              <button>登陆</button>
            </form>
          </div>
        )
      }
      //触发事件时在执行return里的 
      saveFormData = (v) => {
        //console.log(v);
        return (e) => {
          console.log(v,e.target.value);
          this.setState({
            [v]:e.target.value
          })
        }
      }
    }
    ReactDOM.render(<Login />, document.getElementById('app1'))

组件生命周期

基本使用

基本的
componentDidMount
componentWillUnmount
卸载组件ReactDOM.unmountComponentAtNode(document.getElementById(‘app1’))

    class Life extends React.Component {
      // 状态
      state = {
        opacity: 1
      }
      //初始化时 状态更新时
      render() {
        const { opacity } = this.state
        return (
          <div>
            <h2 style={{ opacity: opacity }}>学不会React怎么办?</h2>
            <button onClick={this.death}>不行!</button>
          </div>
        )
      }
      //生命周期 组件挂载完毕时调用 不需要等于箭头函数 因为不是拿来用作回调
      //组件  完成   挂载
      componentDidMount() {
        this.timer = setInterval(() => {
          let { opacity } = this.state
          opacity += 1
          if (opacity <= 0) opacity = 1
          this.setState({
            opacity
          })
        }, 200);
      }
      //组件  即将  卸载
      componentWillUnmount() {
        clearTimeout(this.timer)
      }
      death = () => {
        //也可以在这里关闭定时器

        //卸载组件
        ReactDOM.unmountComponentAtNode(document.getElementById('app1'))
      }
    }
    //渲染到页面挂载到页面mount 卸载unmount
    ReactDOM.render(<Life />, document.getElementById('app1'))

组件的顺序

constructor
componentWillMount  组件将要挂载
render
componentDidMount  组件挂载完毕


使用ReactDOM.unmountComponentAtNode()卸载之前会调这个函数
componentWillUnmount 组件即将卸载

当调用setState()时

相当于组件更新的阀门 默认返回true 返回假则不往下走 返回真则继续往下走
shouldComponentUpdate( )

组件将要更新的钩子
componentWillUpdate

render

组件更新完毕的钩子
componentDidUpdate

当调用forceUpdate()强制更新时

比更新少了个阀门
不想对状态做出修改 就像让你更新一下就用这个

组件将要更新的钩子
componentWillUpdate

render

组件更新完毕的钩子
componentDidUpdate

总结代码

    //创建组件
    class Life extends React.Component {

      //构造器
      constructor(props) {
        super(props)
        console.log('Life-constructor');

        // 初始化状态
        this.state = {
          count: 0
        }
      }
      render() {
        console.log('Life-render');
        const {
          count
        } = this.state
        return (
          <div>
            <h2>当前求和为{count}</h2>
            <button onClick={this.add}>加一</button>
            <button onClick={this.force}>强制更新</button>
          </div>
        )
      }
      //组件将要挂载
      componentWillMount() {
        console.log('Life-componentWillMount');
      }
      //组件  完成   挂载
      componentDidMount() {
        console.log('Life-componentDidMount');
      }



      //------------------------------------------------------------------------------

      // 当setState调用 默认返回true 返回假则不往下走 
      shouldComponentUpdate() {
        console.log('Life-shouldComponentUpdate');
        return true
      }
      // 组件将要更新的钩子
      componentWillUpdate() {
        console.log('Life-componentWillUpdate');
      }
      //组件更新完毕的钩子
      componentDidUpdate() {
        console.log('Life-componentDidUpdate');
      }

      //------------------------------------------------------------------------------



      // 当要卸载前
      componentWillUnmount() {
        console.log('Life-componentWillUnmount');
      }

      add = () => {
        let { count } = this.state
        this.setState({
          count: count + 1
        })
      }
      force = () => {
        this.forceUpdate()
      }
    }
    //渲染组件
    ReactDOM.render(<Life />, document.getElementById('app1'))

父组件render流程

先贴代码 A组件的name传给B组件展示

    class A extends React.Component {
      state = {
        carName: '奔驰'  //A组件只定义不展示
      }
      render() {
        return (
          <div>
            <div>A</div>  {/*未展示A状态里的汽车*/}
            <button onClick={this.changeCar}>换车</button>
            <B {...this.state} />
          </div>
        )
      }
      changeCar = () => {
        this.setState({
          carName: '奥托'
        })
      }
    }
    class B extends React.Component {
      componentWillReceiveProps(props){
        console.log('子组件将要接受标签属性(参数)componentWillReceiveProps',props);
      }
      render() {
        console.log(this.props);
        return (
          <div>
            <div>B</div>
            <div>{this.props.carName}</div> {/*B组件里展示A传过来的汽车*/}
          </div>
        )
      }
    }
    //渲染组件
    ReactDOM.render(<A />, document.getElementById('app1'))

生命周期

一旦父组件的render执行,那么子组件就会执行
组件   将要   接受  标签属性(参数)
componentWillReceiveProps(props)

但是你会发现 父组件的props第一次传过来(也就时页面第一次渲染)是不会调用的
再次该变父组件的props值 则就会调用这个钩子函数



然后就和更新状态一样
shouldComponentUpdate( ) 阀门

组件将要更新的钩子
componentWillUpdate

render

组件更新完毕的钩子
componentDidUpdate(preProps,preState)

新版本的生命周期

新版本即将废弃的钩子

以下三个前面要加UNSAFE_
componentWillMount
componentWillUpdate
componentWillReceiveProps(props)
但是会废弃的 所以最好新版本不要用这三个

新版本的钩子直接看钩子图 新加的使用场景极其罕见
所以最常用的只有三个
DidMount
DidUpdate
WillUnmount

新版钩子的用法

新版本的钩子
static getDerivedStateFromProps(props,state){
	//返回一个state对象 或者null
	//如果返回state对象 则该组件state等于对象值且值不能修改
	//若state的值在任何时候都取决于props,那么可以使用,但是不是必须
	//因为构造器里也可以收到
	return null
	return {props}
	return {name:"你好"}
}
//但是此组件会让代码难以维护等等 所以使用场景极其罕见



这个函数在更新前做点事情 比如拿到更新前节点的滚动条数据
getSnapshotBeforeUpdate(){
	return 快照值 返回值会传给componentDidUpdate(preProps,preState,v)用第三个参数接
}

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Loveyless

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值