【React学习笔记】第二章:React面向组件编程

1.安装React 开发者工具调试

在这里插入图片描述
1.在Chrome应用商店中添加扩展程序:React Developer Tools
2.在github上下载压缩包:https://github.com/facebook/react-devtools/tree/v3

安装好之后运行react项目按F12打开浏览器控制台会多出两个tab栏:
在这里插入图片描述

● Components: 观察网页由多少个组件组成,每个组件有什么属性……
● Profiler: 用于记录网站性能,渲染时间,哪个组件加载的最慢……
本地调试使用 Liver Server 打开, alt b是打开本地磁盘的内容调试工具无法使用

2.React中组件的基本理解和使用

定义组件的两种方式:函数式组件和类式组件
-复杂组件:有状态(state)
-简单组件:无状态(state)

2.1 函数式组件

示例图:
在这里插入图片描述
代码

<body>
  <!-- 准备好一个容器 -->
  <div id="test"></div>
  <!-- 引入react核心库,全局多了React对象 -->
  <script type="text/javascript" src="../js/react.development.js"></script>
  <!-- 引入react-dom 用于支持react操作dom,全局多了ReactDOM对象  -->
  <script type="text/javascript" src="../js/react-dom.development.js"></script>
  <!-- 引入babel 用于将jsx转为js -->
  <script type="text/javascript" src="../js/babel.min.js"></script>

  <script type="text/babel">
    // 1.创建函数式组件
    function Demo() {
      return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>
    }
    // 2.渲染组件到页面
    ReactDOM.render(<Demo />, document.getElementById('test'))
  </script>
</body>

注意:
1.函数首字母必须大写,必须有返回值,必须要写组件标签,写好函数式组件后,由React调用。
2.函数中的this是 undefined,因为 babel 翻译后开启了严格模式(特点:禁止自定义函数的this指向window)
3.执行了ReactDom.render(<Demo / >, document.getElementById('test')),发生了什么?
-1.React解析组件标签,找到了 Demo组件。
-2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面中。

2.2 类式组件

示例图:
在这里插入图片描述
代码:

  <script type="text/babel">
    // 1.创建类式组件: 必须继承React.Component,必须有render且有返回值
    class MyComponents extends React.Component {
      // render是放在MyComponents的原型对象上,供实例使用。
      // render中的this是【MyComponent的实例对象】(MyComponent组件实例对象)。
      render () {
        console.log('render中的this', this)
        return <h2>我是通过类式定义的组件(适用于【复杂组件】的定义)</h2>
      }
    }
    // 2.渲染组件到页面
    ReactDOM.render(<MyComponents/>, document.getElementById('test'))
  </script>

注意:
1.必须继承React.Component,必须有render且必须有返回值
2.render是放在MyComponents的原型对象上,供实例使用;render中的this是【MyComponent的实例对
象】(MyComponent组件实例对象)。
3.执行了ReactDOM.render(<MyComponents/>, document.getElementById('test'))后,发生了什么?
  -1.React解析组件标签,找到了MyComponents组件
  -2.React发现组件是使用类定义的,随后new出来MyComponents的实例,并通过该实例调用原型上的
render方法
  -3.将render返回的虚拟DOM转为真实DOM,随后呈现在页面中

3.组件实例的三大核心属性—state

3.1 state的基本使用

状态(state)即数据,是组件内部的私有数据,只能在组件内部使用
state的值是对象,表示一个组件中可以有多个数据

初始化状态:

this.state = { count:0 }

获取state

<div>{this.state.count}</div>

修改state

this.setState({
   count:this.state.count + 1
})

3.2 案例:点击标题切换内容

描述:点击标题文字,切换内容"凉爽" <=>“炎热”
示例图:
在这里插入图片描述
代码

    class Weather extends React.Component {
      // 构造器调用了几次? —— 1次
      constructor(props){
        super(props);
        // 初始化状态
        this.state = {
          isHot: true,
        }
        // 解决changeWeather中的this指向问题: 把this改成Weather实例对象并生成一个新的函数
        this.toWeather = this.changeWeather.bind(this)
      }
      // render 调用了几次?——1+n次:初始化一次加状态更新的次数
      render() {
        // 读取状态
        const { isHot } = this.state;
        return <h1 onClick={this.toWeather}>今天天气很{isHot ? '炎热' : '凉爽'}</h1>
      }
      // changeWeather调用了几次? ——点几次调用几次
      changeWeather() {
        // changeWeather放在了Weather的原型对象上,供实例使用
        // 由于changeWeather是作为onClick的回调,所以不是通过实例调用的,是直接调用的
        // 类中的方法默认开启局部的严格模式,所以changeWeather 中的this是undefined
        // console.log('点击了标题—changeWeather',this)

        // 获取原来的isHot值
        const isHot = this.state.isHot;
        // 状态不可直接更改,要借助一个内置的API-setState,不是替换是合并
        this.setState({ isHot: !isHot})
      }
      
    }
    ReactDOM.render(<Weather />, document.getElementById('test'))
    const title = document.getElementById('title');

总结:
1.构造器:this是该类的实例对象
 1)用于初始化状态,只会调用一次
 2)用于解决自定义函数中的this指向的问题
2.render:this是该类的实例对象
 1)用于读取state状态,根据状态的值做展示
 2)render中的 this 就是组件的实例对象
3.changeWeather:
 1)主要做两件事:获取state中isHot的值 ,更改状态
 2)状态不可直接更改,要借助一个内置的API—setState,且此操作是合并不是替换
4.为什么render中的this是类的实例对象?
ReactDOM.render(<Weather />, document.getElementById('test'));
 react中 new Weather() 拿到了这个实例(w1),然后通过w1.render进行调用
5.为什么 changeWeather 中的this是undefined?
 这是自定义的点击事件,是作为onClick的回调,所以不是通过实例调用的。由于类中的方法默认开启局部的
 严格模式,所以changeWeather 中的this是undefined(解决方式是在构造器中使用bind)

3.3 State的简写方式

    class Weather extends React.Component {
      constructor(props){
        super(props);
        // this.changeWeather = this.changeWeather.bind(this)
      }
      // 初始化状态
      state = {
        isHot1: true,
      }
      render() {
        const { isHot } = this.state;
        return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'}</h1>
      }
      // 自定义方法:赋值语句 + 箭头函数,这样this就是组件实例对象
      changeWeather = () => {
        console.log(this)
        const isHot = this.state.isHot;
        this.setState({ isHot: !isHot})
      }
}

3.4 总结

1.组件自定义的方法中 this 为 undefined,如何解决?
 -a. 强制绑定this:通过函数对象的bind()
 -b.箭头函数
2.状态数据不能直接修改或更新
3.组件中render方法中的 this 为组件实例对象

4.组件实例的三大核心属性—props

4.1 props的基本使用

组件标签的所有属性都保存在 props 中
作用:
 1.通过标签属性从组件外向组件内传递变化的数据
 2.props 只读,组件内部不能修改 props 数据
使用:
 1.读取某个属性值 this.props.name
 2.扩展属性:将对象的所有属性通过 props 传递
<Person {...person}/>
 3.对props中的属性值进行类型限制和必要性限制
  ● 方式1:(React v15.5 开始已弃用)
  Person.propTypes = { name: React.PropTypes.string.isRequired }
  ● 方式2:(新,需引入 prop-types.js)
  Person.propTypes = { name: PropTypes.string.isRequired }
 4.设置默认属性值
Person.defaultProps = { age: 18, sex: '男' }
 5.组件类的构造函数 - constructor
 super()是否接收并传递props取决于是否希望在构造器中通过 this 访问 props,类中的构造器能省略就省略
  ● super(props):接收props并传递,可以通过实例访问 props
  ● super(): 无法通过实例访问props, 构造器中this.props 为undefined

    // 创建组件
    class Person extends React.Component {
      render () {
        const { name, age, sex } = this.props;
        return (
          <ul>
            <li>姓名:{ name }</li>
            <li>性别:{ sex }</li>
            <li>年龄:{ age }</li>
          </ul>
        )
      }
    }
// 传递 props 方式1:
ReactDOM.render(<Person name="tom" age="18" sex="" />, document.getElementById('test'))
ReactDOM.render(<Person name="jerry" age="20" sex="" />, document.getElementById('test2'))

// 传递 props 方式2:批量传递
const p = { name: 'tom', age: 18, sex: '女' }
ReactDOM.render(<Person {...p} />, document.getElementById('test'))

4.2 对props进行限制

4.2.1 对标签属性进行类型、必要性的限制以及默认值的设置

对标签属性进行类型、必要性的限制

   Person.propTypes = {
    name: PropTypes.string.isRequired,
    sex: PropTypes.string,
    age: PropTypes.number,
    speak: PropTypes.func
   }

指定默认的标签属性值

   Person.defaultProps = {
    sex: '不男不女',
    age: 18
   }
4.2.2 示例
    // 创建组件
    class Person extends React.Component {
      render () {
        const { name, age, sex } = this.props;
        return (
          <ul>
            <li>姓名:{ name }</li>
            <li>性别:{ sex }</li>
            <li>年龄:{ age }</li>
          </ul>
        )
      }
    }

   // 对标签属性进行类型、必要性的限制
   Person.propTypes = {
    name: PropTypes.string.isRequired,
    sex: PropTypes.string,
    age: PropTypes.number,
    speak: PropTypes.func
   }
  //  指定默认的标签属性值
   Person.defaultProps = {
    sex: '不男不女',
    age: 18
   }
  ReactDOM.render(<Person name="jerry" age={20} sex="" speak = {speak} />, document.getElementById('test2'))
  ReactDOM.render(<Person name="老刘" />, document.getElementById('test3'))
    
  function speak(){
    console.log('我说话了')
  }

4.3 props的简写方式

把对标签属性的限制及默认值的设置写在类中
不加 static 是加给类的实例,加上static是加在类本身

    class Person extends React.Component {
      // 对标签属性进行类型、必要性的限制
      static propTypes = {
        name: PropTypes.string.isRequired,
        sex: PropTypes.string,
        age: PropTypes.number,
        speak: PropTypes.func
      }
      //  指定默认的标签属性值
      static defaultProps = {
        sex: '男',
        age: 18
      }
      render () {
        const { name, age, sex } = this.props;
        return (
          <ul>
            <li>姓名:{ name }</li>
            <li>性别:{ sex }</li>
            <li>年龄:{ age }</li>
          </ul>
        )
      }
    }

    ReactDOM.render(<Person name="Jerry" />, document.getElementById('test'))

4.4 函数式组件使用props

通过函数可以接收参数的特性

    // 对标签属性进行类型、必要性的限制
    Person.propTypes = {
        name: PropTypes.string.isRequired,
        sex: PropTypes.string,
        age: PropTypes.number,
        speak: PropTypes.func
    }
    //  指定默认的标签属性值
    Person.defaultProps = {
        sex: '男',
        age: 18
    }
    // 定义函数式组件
    function Person (props) {
      const { name, age, sex } = props;
      return (
          <ul>
            <li>姓名:{ name }</li>
            <li>性别:{ sex }</li>
            <li>年龄:{ age }</li>
          </ul>
        )
    }
    ReactDOM.render(<Person name="Jerry" />, document.getElementById('test'))

5.组件实例的三大核心属性—refs与事件处理

5.1使用ref的三种方式

  • 字符串形式
    • 定义:<input ref="input1" />
    • 使用:const { value } = this.refs.input1;
  • 回调函数形式
    • 定义:<input ref={(c) => this.input1 = c} />
    • 使用:const { value } = this.input1
  • createRef
    • 定义:<input ref={this.myRef} />
    • 使用:const { value } = this.myRef.current
5.1.1 字符串形式的ref

在条件允许的情况下,尽可能避免使用字符串形式的ref

class Demo extends React.Component{
     render() {
       return (
          <div>
             <input ref="input1" type="text" placeholder="点击按钮提示数据" />
             <button onClick={this.showData}>点击提示左侧数据</button>              
          </div>
        )
    }
      showData = () => {
         const { value } = this.refs.input1;
         alert(value)
      }
}
ReactDOM.render(<Demo />, document.getElementById('test'));
5.1.2 回调函数形式的ref
class Demo extends React.Component{
    render() {
       return (
          <div>
            <input ref={(c) => this.input1 = c } type="text" placeholder="点击按钮提示数据" />
            <button onClick={this.showData}>点击提示左侧数据</button>              
          </div>
        )
    }
    showData = () => {
        const { value } = this.input1;
        alert(value)
    }
}
ReactDOM.render(<Demo />, document.getElementById('test'));

扩展: ref={(c) => this.input1 = c内联形式的ref在创建时执行一次,更新页面时会执行两次。可以使用class 的绑定函数的方式来避免这个问题。但内联形式还是常用的。

class Demo extends React.Component{
    // 通过将ref的回调定义成 class 的绑定函数的方式可以避免每次渲染时会被执行两次的问题
    saveInput = (c) => {
       this.input1 = c;
    }
    render() {
       return (
          <div>
            <input ref={this.saveInput} type="text" placeholder="点击按钮提示数据" />
            <button onClick={this.showData}>点击提示左侧数据</button>              
          </div>
        )
    }
    showData = () => {
        const { value } = this.input1;
        alert(value)
    }
}
ReactDOM.render(<Demo />, document.getElementById('test'));
5.1.3 createRef的形式

1.React.createRef()调用后可以返回一个容器,该容器可以存储ref所标识的节点;
2.this.myRef是使用createRef创建的一个容器,把当前ref所在的节点(input)直接存储到这个容器里
3.需要几个ref就需要创建几个

class Demo extends React.Component{
    myRef = React.createRef();
    showData = () => {
        const { value } = this.myRef.current;
        alert(value)
    }
    render() {
       return (
          <div>
            <input ref={this.myRef} type="text" placeholder="点击按钮提示数据" />
            <button onClick={this.showData}>点击提示左侧数据</button>              
          </div>
        )
    }
}
ReactDOM.render(<Demo />, document.getElementById('test'));

5.2 事件处理

5.2.1事件绑定

1.通过 onXxx属性指定事件处理函数(注意大小写)
 a. react事件绑定语法与DOM事件语法相似
  - 语法:on + 事件名称 = { 事件处理程序} , 如: onClick = { () =>{} }
  - React事件采用驼峰命名法,如:onMouseEnter、onFocus
 a. React使用的是自定义(合成)事件,而不是使用的原生DOM事件——为了更好的兼容性
 b. React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)——为了高效
2.通过event.target得到发生事件的DOM元素对象 ——不要过度使用ref

示例:

// 类式组件
class Header extends Component {
  // 按钮点击事件
  handleClick = (e) => {}
  render() {
    return (
      <div className="todo-header">
        <button onClick={this.handleClick}></button>
      </div>
    )
  }
}
5.2.2 事件对象

可以通过事件处理程序的参数获取到事件对象

function handleClick(e){
    e.preventDefault()
    console.log('事件对象:',e)
}
// 绑定事件
<a onClick={handleClick}></a>

6.组件生命周期

6.1 生命周期图(旧)

在这里插入图片描述
生命周期的三个阶段(旧)

  1. 初始化阶段执行的生命周期(4个)ReactDOM.render() 触发
    1.constructor()
    2.componentWillMount() — 组件将要挂载时的钩子
    3.render() — 组件挂载的钩子
    4.componentDidMount() — 组件挂载完毕的钩子
  2. 更新阶段执行的生命周期(4个) 由组件内部this.setState() 或父组件重新渲染 render 触发
    1.shouldComponentUpdate — 控制组件是否更新的钩子,若返回 false,则下面的都不执行
    2.componentWillUpdate — 组件将要更新的钩子
    3.render()
    4.componentDidUpdate() — 组件更新完毕的钩子
  3. 卸载组件执行的生命周期(1个)ReactDOM.unmountComponentAtNode() 触发
    1.componentWillUnmount() — 组件将要卸载

作为子组件时,会先执行这个生命周期

  • componentWillReceiveProps() — 组件将要接收新的props的钩子,第一次不执

shouldComponentUpdate返回false执行的生命周期(1个)

  • shouldComponentUpdate()

forceUpdate() 强制更新执行的生命周期(3个)

  • componentWillUpdate()
  • render()
  • componentDidUpdate()

常用的生命周期

  1. componentDidMount()
    做一些初始化的事:开启定时器、发送网络消息、订阅消息
  2. componentWillUnmount()
    做一些收尾的事:关闭定时器、取消订阅消息

6.2 生命周期(新)

react 版本17.0.1

在这里插入图片描述

生命周期的三个阶段(旧)

  1. 初始化阶段执行的生命周期(4个)ReactDOM.render() 触发
    1.constructor()
    2.getDerivedStateFromProps()
    3.render()
    4.componentDidMount()
  2. 更新阶段执行的生命周期(4个) 由组件内部this.setState() 或父组件重新渲染 render 触发
    1.getDerivedStateFromProps
    2.shouldComponentUpdate
    3.render()
    4.getSnapshotBeforeUpdate()
    5.componentDidUpdate()
  3. 卸载组件执行的生命周期(1个)ReactDOM.unmountComponentAtNode() 触发
    1.componentWillUnmount()
static getDerivedStateFromProps (props, state) {
  console.log('getDerivedStateFromProps')
  return null;
}

getSnapshotBeforeUpdate () {
  console.log('getSnapshotBeforeUpdate');
  return null;
}

componentDidUpdate (preProps, preState, snapshotValue) {
  console.log('componentDidUpdate', preProps, preState, snapshotValue)
}

6.3 总结

重要的生命周期:

  1. render: 初始化渲染或重新渲染调用
  2. componentDidMount: 开启监听,发送ajax请求
  3. componentWillUnmount: 清理定时器……

即将废弃三个生命周期钩子,下一个大版本需要加前缀才能使用

  1. UNSAFE_componentWillMount()
  2. UNSAFE_componentWillReceiveProps()
  3. UNSAFE_componentWillUpdate()

新旧对比(17.0.1):

  • 废弃了三个生命周期钩子:
  1. componentWillMount()
  2. componentWillReceiveProps()
  3. componentWillUpdate()
  • 新增了两个生命周期钩子:
  1. getDerivedStateFromProps()
    a. 要定义为静态方法,两个参数:props, state
    b.要有返回值:null 或者 状态对象
    c.场景:state的值在任何时候都取决于 props
  2. getSnapshotBeforeUpdate()
    a.任何返回值将作为参数传递给componentDidUpdate()
    b.场景:组件在发送更改之前从DOM捕获一些信息(滚动位置)

7.关于表单处理

7.1 受控组件

其值受到React控制的表单元素

  • html中表单元素时可输入的,也就是有自己的可变状态
  • 而React中可变状态通常保存在state中,并且只能通过setState()方法来修改
  • React将state与表单元素值value绑定到一起,由state的值来控制表单元素的值

步骤:
1.在state中添加一个状态,作为表单元素的value值(控制表单元素值的来源)
2.给表单元素绑定change事件,将表单元素的值设置为state的值(控制表单元素值的变化)

<input type="text" value = { this.state.txt } onchange={this.handleContent}>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值