React组件使用

45 阅读13分钟
  • 组件基本介绍

    • 组件是 React 中最基本的内容,使用React就是在使用组件
    • 组件表示页面中的部分功能
    • 多个组件可以实现完整的页面功能
    • 组件特点:可复用,独立,可组合

image.png # React 创建组件的两种方式

## 函数组件

> 函数组件:使用JS的函数或者箭头函数创建的组件

-   为了区分普通标签,函数组件的名称必须 `大写字母开头`
-   函数组件`必须有返回值`,表示该组件的结构
-   如果组件不渲染任何内容,可以返回 null。不能返回undefined
-   React组件对大小写敏感,使用组件同样要使用大写单词开头。

使用函数创建组件

```
function Hello () {
    return (
        <div>这是我的函数组件</div>
    )
}
```

使用箭头函数创建组件

```
const Hello = () => <div>这是一个函数组件</div>
```

使用组件

```
ReactDOM.render(<Hello />, document.getElementById('root'))
```

## 类与继承

### class 基本语法

-   在 ES6 之前通过构造函数创建对象

-   在 ES6 中新增了一个关键字 class, 类 和构造函数类似,用于创建对象

    -   类与对象的区别
    -   类:指的是一类的事物,是个概念,比如车 手机 水杯等
    -   对象:一个具体的事物,有具体的特征和行为,比如一个手机,我的手机等, 类可以创建出来对象。

-   类创建对象的基本语法

    -   基本语法`class 类名{ }`
    -   构造函数 `constructor` 的用法,创建对象
    -   在类中提供方法,直接提供即可
    -   💥💥在类中不需要使用分号,分隔(Vue因为是对象才需要分号分隔)

### extends 实现继承

-   extends 基本使用
-   类可以使用它继承的类中所有的成员(属性和方法)
-   类中可以提供自己的属性和方法
-   注意:如果想要给类中新增属性,必须先调用 super 方法

**class语法总结:**

0.  作用: 创建对象,等同于构造函数。

0.  类与对象:

    0.  是指一类事物,比如程序员,指的是一类群体。
    0.  对象是指具体的事物。前端开发,是程序员这类人中的一种。后台开发,也是程序员的一种。
    0.  举例,使用FrontEnd与BackEnd,继承Programmaer。

0.  总结


    0.  **用途:继承方法与属性**,React.Component能提供其它的钩子方法。
    0.  **注意:可以指定属性**,用=号的方式。
    0.  **强调:class不是对象。**

### 注意

0.  💥💥在类中不需要使用,分隔(Vue因为是对象才需要分号分隔)
0.  类组件需要继承于 `React.Component`

类组件

类组件:使用ES6的class语法创建组件

约定1:类组件的名称必须是大写字母开头

约定2:类组件应该继承React.Component父类,从而可以使用父类中提供的方法或者属性

约定3:类组件必须提供render方法,并返回组件的结构,如无需要返回的结构,返回null

  • 定义组件
class Hello extends React.Component {
  render() {
    return <div>这是一个类组件</div>
  }
}
​
  • 使用组件
ReactDOM.render(<Hello />, document.getElementById('root'))

注意:

  1. class 组件名称 extends React.Component 除了组件名称,都是固定写法
  2. render函数是钩子函数,名称是固定的,表示组件渲染一段html内容。
  3. 使用上,类组件与函数式组件,完全相同

将组件提取到单独的js文件中

目标:能够将react组件提取到独立的js文件中

思考:项目中的组件多了之后,该如何组织这些组件呢?

  • 选择一:将所有组件放在同一个JS文件中

  • 选择二:👍将每个组件放到单独的JS文件中

    • 推荐选择二- 组件作为一个独立的个体,一般都会放到一个单独的 JS 文件中

实现方式

  1. 创建Hello.js
  2. 创建组件(函数 或 类)
  3. 在 Hello.js 中导出该组件
  4. 在 index.js 中导入 Hello 组件
  5. 渲染组件

核心代码

// index.js
import Hello from './Hello'
// 渲染导入的Hello组件
ReactDOM.render(<Hello />, root)
​
// Hello.js
import { Component } from 'react'
class Hello extends Component {
  render() {
    return <div>Hello Class Component!</div>
  }
}
// 导出Hello组件
export default Hello

类组件的状态 - state

目标:掌握react类组件中如何提供状态以及渲染状态

内容

  • 状态state即数据。(同Vue的data)

    • 类似Vue中的data函数。
    • React同样是数据驱动视图 更新UI
  • 状态state 是组件内部的私有数据,独立作用域,(同Vue的data)

  • 状态 state 的值是对象,表示一个组件中可以有多个数据

核心代码

  • 创建 state
class Hello extends Component {
  // 为组件提供状态
  state = {
    count: 0,
    list: [],
    isLoading: true
  }
​
  render() {
    return (
      <div>计数器</div>
    )
  }
}
  • 读取状态:通过 this.state 来获取状态
class Hello extends Component {
  // ...
  render() {
    // 通过 this.state 来访问类组件的状态
    return (
      <div>计数器:{this.state.count}</div>
    )
  }
}

总结:

  1. state的作用:声明数据。 React中同样是数据驱动视图,数据变化导致UI更新。
  2. 声明方式state = {} 是固定的写法,独立作用域。
  3. 使用方式:通过this.state.属性名 的方式来访问
  4. this指向组件实例对象。

注意:

  1. 💥 class组件中才有声明state 的写法,函数组件中没有
  2. 💥 早期,只有类组件,可以定义状态

react插件的安装(没网)

安装谷歌插件react-devtools

组件类型-有状态组件和无状态组件

  • 无状态组件 : 早期,函数组件是不能自己提供数据【前提:基于hooks之前说的】

  • 有状态组件 : 类组件可以自己提供数据,组件内部的状态(数据如果发生了改变,内容会自动的更新),数据驱动视图

  • 状态组件-容器组件

    • 请求的数据保存在组件内。
  • 无状态组件-展示组件

    • 负责样式与结构的渲染。

组件类型-

1. 状态组件和无状态组件。
2. 容器组件和展示组件。

注意

  • 现在函数组件也可以定义状态。【早期,函数组件是没有状态的】
  • 在复杂的项目中,一般都是由函数组件和类组件共同配合来完成的。【class增加了使用者的负担,所以后来有了hooks】

事件处理

问题:React中怎么给DOM绑定一个事件呢?

注册事件

目标: 掌握react中如何注册事件

内容

  • React注册事件与DOM的事件语法非常像
  • 语法on+事件名 ={事件处理程序} 比如onClick={this.handleClick}
  • 注意:React事件采用驼峰命名法,比如onMouseEnter, onClick

核心代码

class App extends React.Component {
  handleClick() {
    console.log('点击事件触发')
  }
  
  render() {
    return (
      <div>
        <button onClick={this.handleClick}>点我</button>
      </div>
    )
  }
}

总结:

  1. React事件采用驼峰命名法,比如onMouseEnter, onClick
  2. class中自定义方法,通过this.方法名

注意:🔔

  1. React事件绑定一定是方法,不能是方法调用。如:onClick = {alert('aaa')}
  2. 少量的代码,可以通过箭头函数,来处理。如:onClic = { () => alert('aaa') }

事件对象

  • 可以通过事件处理函数的形参获取到事件对象
function handleClick(e) {
    e.preventDefault()
    console.log('事件对象', e)
}
<a onClick={this.handleClick}>点我,不会跳转页面</a>

事件传值

  • 方法:包一层箭头函数,在箭头函数中,调用处理函数。
  • 记忆口诀:加个箭头,变成箭头函数。

写法:

  • 完整写法
<a 
    onClick={(e) => {
        this.handleClick(e, 'hello React')
    }}
>点我,不会跳转页面</a>
  • 简写-推荐:省略了花括号,更利于阅读
<a onClick={(e) => this.handleClick(e, 'hello React') }>点我,不会跳转页面</a>

this指向问题

事件处理程序中的this指向的是undefined

  1. render方法中的this没有问题,指向当前react组件实例。
  2. 只有事件处理函数中的this有问题
class App extends React.Component {
  state = {
    msg: 'hello react'
  }
​
  handleClick() {
    console.log(this.state.msg)
  }
​
  render() {
    return (
      <div>
        <button onClick={this.handleClick}>点我</button>
      </div>
    )
  }
}

this指向问题解决方案

方案1class箭头函数方法 - 推荐 👍 
方案2:render中使用箭头函数   
方案3:bind修改this指向
  • 解决方案1:class箭头函数方法 - 👍
class App extends React.Component {
  state = {
    msg: 'hello react'
  }
​
  handleClick = () => {
    console.log(this.state.msg)
  }
  render() {
    return (
      <div>
        <button onClick={this.handleClick}>点我</button>
      </div>
    )
  }
}

注意:这个语法是试验性的语法,但是有babel的转义,所以没有任何问题

  • 解决方案2:在render中使用箭头函数

箭头函数的特点:自身没有this,访问的是外部的this

class App extends React.Component {
  state = {
    msg: 'hello react'
  }

  handleClick() {
    console.log(this.state.msg)
  }

  render() {
    return (
      <div>
        <button onClick={() => { console.log(this.state.msg) }>点我</button>
        <button onClick={() => {this.handleClick()}}>点我</button>
      </div>
    )
  }
}

缺点:会把大量的js处理逻辑放到JSX中,将来不容易维护

  • 解决方案3:使用bind
class App extends React.Component {
  state = {
    msg: 'hello react'
  }
  handleClick() {
    console.log(this.state.msg)
  }
  render() {
    return (
      <div>
        <button onClick={this.handleClick.bind(this)}>点我</button>
      </div>
    )
  }
}

setState修改状态

目标: 掌握react中如何通过setState修改状态

内容

  • 语法:this.setState({ 要修改的部分数据 })

  • setState() 作用:1 修改 state 2 更新 UI

  • 思想:数据驱动视图,也就是只需要修改数据(状态)那么页面(视图)就会自动刷新

    • 核心:数据!!!
    • 从现在开始,我们关心的是如何修改数据,而不再是关心如何修改DOM
    • 并且,注意:尽量避免直接手动 DOM(通过 document.querySelector() 获取到到DOM对象然后再操作) 操作!!!
  • 注意:不要直接修改 state 中的值,这是无效的

核心代码

class Hello extends Component {
  state = {
    count: 0
  }

	handleClick = () => {
    this.setState({
      count: 10
    })
  }  

  render() {
    return (
      <div>
        <h1>计数器:{this.state.count}</h1>
        <button onClick={this.handleClick}>+1</button>
      </div>
    )
  }
}

// 在 count 当前值的基础上加 1
this.setState({
  count: this.state.count + 10
})

总结

  1. 能直接修改 state 的值吗?不能
  2. 如何修改 React 组件中的状态?setState()
  3. 语法this.setState({要修改的数据名: 新的值})

React核心理念-状态不可变

  • 不能直接修改state的值,而是提供新的值
  • 直接修改react中state的值,组件并不会更新
state = {
  count: 0,
  list: []
}
// ❌错误操作:直接修改值的操作
this.state.count++
this.state.list.push('a')

// ✅正确操作:创建新的值的操作
this.setState({
    count: this.state.count + 1,
    list: [...this.state.list, 'b']
})

练习-购物车加减

要求:加减按钮,绑定同一个处理函数

效果如下:

image.png

基本的组件代码:

class App extends React.Component {
  state = {
    count: 0,
  };

  render() {
    return (
      <div>
        <p>当前数值为:{this.state.count}</p>
        <hr />
        <button>+1</button>
        <span>{this.state.count}</span>
        <button>-1</button>
      </div>
    );
  }
}

表单处理

我们在开发过程中,经常需要操作表单元素,比如获取表单的值或者是设置表单的值。

react中处理表单元素有两种方式:

  • 受控组件-表单元素的value受到state的控制,(类似Vue中的v-model)
  • 非受控组件(DOM操作)

受控组件基本概念

  • React中将state中的数据与表单元素的value值绑定到了一起,由state的值来控制表单元素的值
  • 但是在react中,可变状态通常是保存在state中的,并且要求状态只能通过setState进行修改。
  • 受控组件:value值受到了statet控制的表单元素

image.png

受控组件使用步骤

  1. 在state中添加一个状态,作为表单元素的value值(控制表单元素的值)
  2. 给表单元素添加change事件,设置state的值为表单元素的值(控制值的变化)
class App extends React.Component {
  state = {
    msg: 'hello react'
  }

  handleChange = (e) => {
    this.setState({
      msg: e.target.value
    })
  }

  render() {
    return (
      <div>
        <input type="text" value={this.state.msg} onChange={this.handleChange}/>
      </div>
    )
  }
}

总结:

什么是受控组件?

  1. 表单元素。
  2. 🔔由state控制值。
  3. 🔔由setState来改变值。

注意:

  1. 👍推荐使用onChange 事件监听用户输入,👎不推荐使用onInput
  2. reactchange 事件等同于html中的input 事件。
  3. react中的blur 事件,等同于html中的change 事件。

常见的受控组件

  • 文本框、文本域、下拉框(操作value属性)
  • 复选框(操作checked属性)
class App extends React.Component {
  state = {
    usernmae: '',
    desc: '',
    city: "2",
    isSingle: true
  }
 
  handleName = e => {
    this.setState({
      name: e.target.value
    })
  }
  handleDesc = e => {
    this.setState({
      desc: e.target.value
    })
  }
  handleCity = e => {
    this.setState({
      city: e.target.value
    })
  }
  handleSingle = e => {
    this.setState({
      isSingle: e.target.checked
    })
  }

  render() {
    return (
      <div>
        姓名:<input type="text" value={this.state.username} onChange={this.handleName}/>
        <br/>
        描述:<textarea value={this.state.desc} onChange={this.handleDesc}></textarea>
        <br/>
        城市:<select value={this.state.city} onChange={this.handleCity}>
          <option value="1">北京</option>
          <option value="2">上海</option>
          <option value="3">广州</option>
          <option value="4">深圳</option>
        </select>
        <br/>
        是否单身:<input type="checkbox" checked={this.state.isSingle} onChange={this.handleSingle}/>
      </div>
    )
  }
}

知识点补充-对象属性名称-插值技术

多表单元素的优化

问题:每个表单元素都需要一个单独的事件处理程序,处理太繁琐

优化:使用一个事件处理程序处理多个表单元素

步骤

  • 给表单元素添加name属性,名称与state属性名相同
  • 根据表单元素类型获取对应的值
  • 在事件处理程序中通过[name]修改对应的state
class App extends React.Component {
  state = {
    username: '',
    desc: '',
    city: "2",
    isSingle: true
  }
 
  handleChange = e => {
    let {name, type, value, checked} = e.target
    console.log(name, type, value, checked)
    value = type === 'checkbox' ? checked : value
    console.log(name, value)
    this.setState({
      [name]: value
    })
  }
  render() {
    return (
      <div>
        姓名:<input type="text" name="username" value={this.state.username} onChange={this.handleChange}/>
        <br/>
        描述:<textarea name="desc" value={this.state.desc} onChange={this.handleChange}></textarea>
        <br/>
        城市:<select name="city" value={this.state.city} onChange={this.handleChange}>
          <option value="1">北京</option>
          <option value="2">上海</option>
          <option value="3">广州</option>
          <option value="4">深圳</option>
        </select>
        <br/>
        是否单身:<input type="checkbox" name="isSingle" checked={this.state.isSingle} onChange={this.handleChange}/>
      </div>
    )
  }
}

非受控组件

表单元素的value由DOM对象控制,不受state的控制

思考:

  1. 除了使用state 控制表单的数据,是否有其它的方式?
/**
 * 学习目标:了解非受控组件 - 真实DOM具备管理表单数据的能力
 * 含义:非受控组件-组件内,表单的数据可,可以不受state的控制
 */
import React from 'react';
import ReactDOM from 'react-dom';

class App extends React.Component {
  handleClick = () => {
    // 这种不需要state管理数据的表单元素,称之为非受控组件,
    // 获取数据:  
    let data = {
      userName: document.querySelector('#username').value,
      desc: document.querySelector('#desc').value,
      city: document.querySelector('#city').value,
      isSingle: document.querySelector('#isSingle').checked,
    };

    console.log(data, 'data');
  };

  handleChange = (e) => {
    // 修改数据
    console.log(e.target.value, 'value');      
  }

  render() {
    return (
      <div>
        姓名:
        <input type="text" name="username" id="username" />
        <br />
        描述:<textarea name="desc" id="desc"></textarea>
        <br />
        城市:
        <select name="city" id="city">
          <option value="1">北京</option>
          <option value="2">上海</option>
          <option value="3">广州</option>
          <option value="4">深圳</option>
        </select>
        <br />
        是否单身:
        <input name="isSingle" type="checkbox" id="isSingle" />
        <button onClick={this.handleClick}>点击提交到后台</button>
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById('root'));

总结:

  1. 表单元素,它的值由DOM控制,不使用state控制,称为非受控组件。
  2. 表单元素,它的值由state控制,由setState改变,称为受控组件。
  3. **推荐-**使用受控组件,React推荐数据驱动视图。

Ref

作用:获取组件实例、获取DOM (作用与Vue中的Ref相同)

目标:使用Ref获取文本框的DOM元素

使用步骤

  1. 调用React.createRef()方法创建一个ref 对象
  2. 将创建好的ref对象绑定到dom元素上
  3. 通过 ref 对象获取dom元素

核心代码

// 1. 创建ref对象
txtRef = React.createRef()

// 2. 绑定dom元素
<input type="text" ref={this.txtRef}/>

// 3. 通过ref独享获取dom元素    
handleClick = () => {
  console.log(this.txtRef.current.value)
}

总结:

  1. 作用:获取组件实例、DOM对象。 (与Vue中的ref,在作用上相同)

  2. ref 的价值在于获取 组件实例对象

  3. 使用步骤:

    1. 创建ref对象
    2. 绑定到组件或DOM上
    3. 使用this.ref对象.current 获取组件或DOM

综合案例

评论列表案例

静态结构如下:

  1. 新建src/index.js
  2. 新建src/index.css

代码如下,直接复制:

src/App.js

import React from 'react';
import ReactDOM from 'react-dom';

import './index.css';

export default class App extends React.Component {
  state = {
    // 用户名输入框
    username: '',
    // 评论内容
    content: '',
  };

  render() {
    return (
      <div className="app">
        {/* 表单提交的时候,运行发布评论的函数 */}
        <form onSubmit={this.handlePublish}>
          <input className="user" type="text" placeholder="请输入评论人" />
          <br />
          <textarea className="content" cols="30" rows="10" placeholder="请输入评论内容" />
          <br />
          <button>发表评论</button>
          <button type="button">清空评论</button>
        </form>
        <ul>
          <li>
            <h3>评论人: xxx</h3>
            <p>评论内容:xxx</p>
            <button>删除</button>
          </li>
        </ul>
      </div>
    );
  }
}

src/index.css

.app {
  width: 300px;
  padding: 10px;
  border: 1px solid #999;
}

.user {
  width: 100%;
  box-sizing: border-box;
  margin-bottom: 10px;
}

.content {
  width: 100%;
  box-sizing: border-box;
  margin-bottom: 10px;
}

.no-comment {
  text-align: center;
  margin-top: 30px;
}

列表展示功能

 [
      {
        id: 1,
        username: '思聪',
        content: '想你的夜',
      },
      {
        id: 2,
        username: '马芸',
        content: '我对钱不感兴趣',
      },
      {
        id: 3,
        username: '王简林',
        content: '实现一个小目标',
      },
   ],

渲染评论列表(列表渲染)

  • 在state中初始化评论列表数据
  • 使用数组的map方法遍历列表数据
  • 给每个li添加key属性

发表评论功能

获取评论信息,评论人和评论内容(受控组件)

  • 使用受控组件的方式获取评论数据

发表评论,更新评论列表(更新状态)

  • 给list增加一条数据

边界处理

  • 清空内容
  • 判断非空

删除一条评论

  • 给删除按钮,绑定事件处理函数
  • 事件函数中,通过id或index,删除list中一条数据

清空评论功能

  • 给清空评论按钮注册事件
  • 通过setState清空评论列表
  • 没有更多评论的处理