React组件基础,保证最基本的使用,还有一个小demo助力大家学懂

1,012 阅读12分钟

下面我主要从这几个方面来介绍

  • React 组件介绍
  • React 组件的两种创建方式
  • 事件绑定
  • 组件的状态
  • 事件绑定 this 指向
  • 表单处理

React 组件介绍

目标

了解 React 组件的意义 image.png

组件

对特定功能的封装,主要用来对UI进行拆分。

内容

结构 HTML

  • Vue: 没有template,可以用render()
  • 有些组件的意义不在于自己的结构,而在于对其他组件的二次封装

样式 CSS

逻辑 JS

特点

  • 独立
  • 可复用
  • 可组合

分类

  • 基础组件:指inputbutton这种基础标签,以及antd封装过的通用UI组件
  • 业务组件:由基础组件组合成的业务抽象化UI。例如: 包含了A公司所有部门信息的下拉框
  • 区块组件:由基础组件组件和业务组件组合成的UI块
  • 页面组件:展示给用户的最终页面,一般就是对应一个路由规则

React 组件的两种创建方式

目标

了解 React 组件的两种创建方式

  1. 使用 JS 中的函数创建组件
  2. 使用 JS 中的 class 创建组件

示例

import React from 'react'
import ReactDOM from 'react-dom'const title = <h1>react的两种组件</h1>// 定义一个函数式组件
const Com1 = () => {
  return <div>第一个函数式组件</div>
}
​
// 定义一个类组件
class Com2 extends React.Component {
  render () {
    return <div>第一个类组件</div>
  }
}
​
const content = (
  <div>
    {title}
    <Com1 />
    <hr />
    <Com2 />
  </div>
)
​
ReactDOM.render(content, document.getElementById('root'))
​

对比

  1. 类组件比较繁琐
  2. 函数式组件比较简便

目前市面上的项目中都有使用

函数式组件-使用函数创建组件

目标

掌握函数式组件的定义和使用方式

定义组件

使用 JS 的函数(或箭头函数)创建的组件,叫做函数组件

  • 约定1:函数名首字符大写

    必须以大写字母开头,React 据此区分组件普通的 HTML

  • 约定2:必须有返回值

    表示该组件的 UI 结构;如果不需要渲染任何内容,则返回 null

示例:

// 1. 使用普通函数创建组件:
function Hello() {
  return <div>这是我的第一个函数组件!</div>
}
​
function Button() {
  return <button>按钮</button>
}
​
// 2.  使用箭头函数创建组件:
const Hello = () => <div>这是我的第一个函数组件!</div>

使用组件

使用组件就像使用 HTML 标签一样。用函数名称作为组件标签名称

// 使用 双标签 渲染组件:
<Hello></Hello>
​
ReactDOM.render(<Hello></Hello>, root)
​
// 使用 单标签 渲染组件:
<Hello />
ReactDOM.render(<Hello />, root)

小结

  1. 函数式组件本质是一个___?

  2. 函数式组件可以使用箭头函数吗?

  3. 函数式组件的要求

    1. 函数名?
    2. 返回值?

类组件-用class创建组件

目标

掌握类组件的基本用法

定义格式

使用 ES6 的 class 创建的组件,叫做类(class)组件

格式

// import { Component } from 'react'
// class 类名 extends Component {
import React form 'react'
class 类名 extends React.Component {
  // ... 
  render () {
    return 本组件的UI结构
  }
}

注意:

  1. 类名必须以大写字母开头
  2. extends是一个关键字,用来实现类之间的继承。类组件应该继承 React.Component 父类,从而使用父类中提供的方法或属性。
  3. 类组件必须提供 render 方法,render 方法必须有返回值,表示该组件的 UI 结构。render会在组件创建时执行一次

示例

// 导入 React
import React from 'react'
class Hello extends React.Component {
  render() {
    return <div>Hello Class Component!</div> 
  }
}

使用组件

// 导入 React
import React from 'react'
import ReactDom from 'react-dom'
// 1. 定义组件
class Hello extends React.Component {
  render() {
    return <div>Hello Class Component!</div> 
  }
}
​
const content = (<div><Hello/></div>)
​
ReactDOM.render(content, document.getElementById('root'))

使用组件的方式与函数式组件一致:可以使用单标签和双标签

小结

  1. class组件的格式是:class 类名 ___ ____?
  2. 对类组件的名称要求是?
  3. 类组件的内部必须提供 ____ 方法?

有状态组件和无状态组件

目标

理解状态的概念,理解有状态组件和无状态组件的概念

什么是状态(state)

定义:用来描述事物在某一时刻的形态数据。一般称为state。例如:9月23号时书的库存数量; 18岁时人的身高;

特点:

  • 状态能被改变,改变了之后视图会有对应的变化

image.png

作用:

  • 保存数据。例如:要循环生成一份歌曲列表,那要提前准备好歌曲数据吧
  • 为后续更新视图打下基础。如果用户点击了操作,让歌单的内容+1了,视图会自动更新(这是由react库决定的)

有状态组件和无状态组件

有状态组件:能定义state的组件。类组件就是有状态组件。

无状态组件:不能定义state的组件。函数组件又叫做无状态组件

注意:2019年02月06日,rect 16.8中引入了 React Hooks,从而函数式组件也能定义自己的状态了

无状态组件的应用场景

  • 组件本身就不需要有状态。例如:渲染一段固定的内容
  • 组件本身就没有状态,可以从外部传入

小结

  1. 状态:用来描述事物在某一时刻的形态数据
  2. 状态的特点: 能被修改,改了之后对应的视图也能更新。
  3. 函数组件是组件,类组件是组件

类组件的状态

目标

掌握react类组件中定义状态和渲染状态

定义状态

有两种等价的格式:

  1. state = 对象
  2. 在构造函数中用this.state= 对象来做初始化
import React from "react";
export default class Hello extends React.Component {
  // 1. 方式1 state就是状态
  state = {
    list: [{ id: 1, name: "明天会更好" },{ id: 2, name: "难忘今宵" }],
    isLoading: true
  };
  // 2. 方式2 构造函数
  constructor() {
    super() // 在构造函数内部使用this,必须提前调用super
    this.state = {
      list: [{ id: 1, name: "明天会更好" },{ id: 2, name: "难忘今宵" }],
      isLoading: true
    }
  }
}
​

在视图中使用

  render() {
    return (
      <>
        <h1>歌单-{this.state.count}</h1>
        <ul>
          {this.state.list.map((item) => (
            <li key={item.id}>{item.name}</li>
          ))}
        </ul>
        <div>{this.state.isLoading ? "正在加载" : "加载完成"}</div>
      </>
    );
  }

小结

  1. state能改成别的名字?

  2. 定义状态:

    1. state= { 对象}
    2. constructor(){ super(); this.state = { // 对象} }
  3. 使用状态: const { num } = this.state

事件绑定

目标

能掌握react中的事件绑定

为什么要做事件绑定

让页面有交互; 修改数据以更新视图

格式

<元素 事件名1={ 事件处理函数1 } 事件名2={ 事件处理函数2 }  ></元素>

注意:React 事件名采用驼峰命名法,比如:onMouseEnter、onFocus、 onClick ......

示例

import React from 'react'
import ReactDOM from 'react-dom'const title = <h1>react中的事件</h1>
​
​
export default class Hello extends React.Component {
  fn() {
    console.log('mouseEnter事件')
  }
  render() {
    return (
      <div
        onClick={() => console.log('click事件')}
        onMouseEnter={this.fn}
        能处理鼠标进入或者点击事件
      </div>
    )
  }
}
​
const content = (
  <div>
    {title}
    {<Hello />}
  </div>
)
​
ReactDOM.render(content, document.getElementById('root'))
​

注意:

  1. 事件名是小驼峰命名格式

  2. 在类中补充方法

  3. this.fn不要加括号:

    • onClick={ this.fn() } 先调用fn(),然后将fn的执行结果当做click事件的处理函数

别忘记了写this

小结

事件对象

复习导入

如何获取原生dom操作时,如何获取事件对象?

能举例说明事件对象的作用?阻止默认行为

目标

掌握react中的获取事件对象的方法

获取事件对象

react中,通过事件处理函数的形参来获取。

示例

​
  handleClick(e)=> {
    e.preventDefault()
    console.log('单击事件触发了', e)
  }
    render() {
    return (<div>
            <button onClick={(e)=>{console.log('按钮点击了', e)}}>按钮</button>
                    <a href="http://itcast.cn/" onClick={this.handleClick}>武汉黑马</a>
            </div>)  
  }
}

小结

事件对象通过回调函数的参数来获取。

事件处理-this指向问题

目标

了解事件处理程序中this指向异常并能知道原因

问题导入

class App extends React.Component {
  state = {
    msg: 'hello react'
  }
  handleClick() {
    console.log(this) // 这里的this是?undefined
  }
  render() {
    console.log(this) // 这里的this是?
    return (
      <div>
        <button onClick={this.handleClick}>点我</button>
      </div>
    )
  }
}
  • render方法中的this指向的而是当前react组件。
  • 事件处理程序中的this指向的是undefined

分析原因

  • 事件处理程序的函数式函数调用模式,在严格模式下,this指向undefined
  • render函数是被组件实例调用的,因此render函数中的this指向当前组件
class Person(name) {
  constructor(){
    this.name = name
  }
  say() {
    console.log(this)
  }
}
let p1 = new Person('小花')
p1.say()
const t = p1.say
t()
​

总结

  1. class的内部,开启了局部严格模式use strict,所以this不会指向window undefined
  2. onClick={this.fn}中,this.fn的调用并不是通过类的实例调用的,所以值是undefined

事件处理-this指向解决方案

目标

掌握常见的this指向解决方案

有三种方式

解决事件处理程序中this指向问题主要有三种方式

  1. 在外层补充箭头函数
  2. Function.prototype.bind()
  3. class 的实例方法【推荐】

方式1:在外层补充箭头函数

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指向外层作用域中的this

缺点:需要额外包裹一个箭头函数,结构不美观

image.png

方式2:使用bind

复习:

image.png

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>
    )
  }
}

原理:bind能绑定this

image.png

方式3: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>
    )
  }
}

小结

  1. 方式3是最方便的,也是以后用的最多的方式

组件的状态-修改状态

目标

理解setState的格式,会使用setState函数来修改状态

setState

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

作用:

  1. 修改 state
  2. 更新 UI

示例

state = {
  count: 0 
};
​
this.setState({
  count: this.state.count++
})

理解状态不可变

react核心理念之状态不可变

不要直接修改当前状态值,而是创建新的状态值去覆盖老的值。

this.state.count = 100 // 无效

注意事项

不能通过直接修改state中的值来让视图变化,而应该用 __ __ __ __来修改

setState的典型用法

目标

掌握setState的典型应用场景

场景

核心代码

state = {
  name: 'jack',
  assets: [{ id: 1, name: '手机' },{ id: 2, name: '耳机' }],
  skill: ['vue', 'react'], // angular
  info: {
    age: 18,
    city: '武汉'
  }
}
​
render () {
    const { name, assets, skill, info } = this.state
    return (
      <div>
        <h2>姓名:{name}</h2>
        <p>
          年龄:{info.age} 城市:{info.city}
        </p>
        <h3>资产</h3>
        <ul>{assets.map((item) => <li key={item.id}>{item.name}</li>)}</ul>
        <h3>技能</h3>
        <ul>{skill.map((item,idx) => <li key={idx}>{item}</li>)}</ul>
        <button
          onClick={() => {
            this.handleClick()
          }}>
          点我改数据
        </button>
      </div>
    )
  }

任务

  1. 把name改成'小花'
  2. 把age改成20
  3. 把'手机'改成'电脑'
  4. 向skill中添加'angular'
  5. 删除id为2的assets

参考代码

  handleClick = () => {
    // 1. 把name改成'小花'
    this.setState({ name: '小花' })
    
    // 2. 把age改成20
    // this.setState({ info: { ...this.state.info, age: 20 } })
    
    // 3. 把'手机'改成'电脑'
    // const assets = [...this.state.assets]
    // assets[0].name = '电脑'
    // this.setState({ assets })
        
    // 4. 向skill中添加'angular'
    // this.setState({ skill: [...this.state.skill, 'angular'] })
    
    // 5. 删除id为2的assets
  }

获取表单元素的值的两种思路

目标

能够了解获取表单元素的值的两种思路

问题导入

如何获取表单元素(例如输入框)?

初始代码

class App extends React.Component {
  render() {
    return (
      <div>
        <h1>如何获取input中的值</h1>
        <p><input type="text" /></p>
        <button>点击按钮</button>
      </div>
    )
  }
}

思路

有两种思路:

  1. 直接找到表单元素进行dom操作 --> 非受控组件
  2. 将表单元素值与react的state绑定到一起,把用户的修改同步到state中。让组件受到react的控制--> 受控组件

小结

非受控组件-ref

目标

借助于ref,使用原生DOM的方式来获取表单元素的值

ref的使用格式

步骤

  1. 导入方法。import { createRef } from 'react'
  2. 调用createRef方法创建引用,假设名为refDom。 const refDom = createRef()
  3. refDom设置给表单元素的ref属性。<input ref={refDom}/>
  4. 通过refDom.current.value来获取值。console.log(this.refDom.current.value)

内容

  • 受控组件是通过 React 组件的状态来控制表单元素的值
  • 非受控组件是通过手动操作 DOM 的方式来控制
  • 此时,需要用到一个新的概念:ref
  • ref:用来在 React 中获取 DOM 元素

示例代码

// 1. 导入方法
import { createRef } from 'react'class Hello extends Component {
  // 2. 调用createRef方法创建引用
  txtRef = createRef()
​
  handleClick = () => {
    // 4. 通过.current.value来获取值
    console.log(this.txtRef.current.value)
  }
​
  render() {
    return (
      <div>
        <h1>如何获取input中的值-非受控组件-ref</h1>
         {/* 3. 设置给表单元素的ref属性 */}
        <p><input type="text" ref={this.txtRef}/></p>
        <button onClick={handleClick}>获取文本框的值</button>
      <div>
    )
  }
}

小结

受控组件

目标

掌握使用受控组件的方式来获取表单元素的值

如何理解受控

正常情况下,表单元素input是可任意输入内容的,可以理解为input自己维护它的状态(value)

受控组件的思路:

  1. 在state中定义状态
  2. 将state中的状态与表单元素的value值绑定到一起,进而通过state中的状态来控制表单元素的值

受控组件:value值受到了react控制的表单元素

基本步骤

有两个步骤:

  1. 在state中定义状态

  2. 对表单元素做两件事

    1. 设置value为上面定义的状态
    2. 绑定onChange事件,并在回调中通过setState来修改状态值

示例

class App extends React.Component {
  state = {
    // 1. 在state中定义状态
    msg: 'hello react'
  }
​
  handleChange = (e) => {
    this.setState({
      msg: e.target.value
    })
  }
  
  handleClick = ()=> {
    console.log(this.state.msg)
  }
​
  render() {
    return (
      <div>
        <h1>如何获取input中的值-受控组件</h1>
        <p>
        {/* 2. 对表单元素做两件事 */}
            <input type="text"
                            value={this.state.msg}
                            onChange={this.handleChange}
                        />
        </p>
        <button onClick={handleClick}>获取文本框的值</button>
      <div>
    )
  }
}

注意

使用受控组件的方式处理表单元素后,状态的值就是表单元素的值。即:想要操作表单元素的值,只需要操作对应的状态即可

拓展:常见的受控组件

目标

掌握常见的受控组件的在react中的使用

image.png

内容

文本框,文本域,下拉框,复选框

要点

不同类型的表单元素进行受控处理时的格式是不同的:

  1. 文本框、文本域、下拉框:value属性 + onChange事件
  2. 复选框: checked属性 + onChange事件
  3. 单选按钮组:checked属性 + onChange事件
import React from 'react'
import ReactDOM from 'react-dom'class App extends React.Component {
  state = {
    username: '',
    desc: '',
    city: '2',
    isSingle: true,
    gender: '男'
  }
​
  handleName = (e) => {
    this.setState({
      username: 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
    })
  }
​
  hGender = (e) => {
    this.setState({
      gender: e.target.value
    })
  }
​
  render () {
    return (
      <div>
        姓名:<input
          type="text"
          value={this.state.username}
          onChange={this.handleName}
        />
        <br />
        描述:<textarea value={this.state.desc} onChange={this.handleDesc} />
        <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}
        />
        <br />
        <input
          type="radio"
          name="gender"
          value="男"
          checked={this.state.gender === '男'}
          onChange={this.hGender}
        />{' '}
        男
        <input
          type="radio"
          name="gender"
          value="女"
          checked={this.state.gender === '女'}
          onChange={this.hGender}
        />{' '}
        女
      </div>
    )
  }
}
​
ReactDOM.render(<App />, document.getElementById('root'))
​

综合案例-B站评论列表

完成交互功能

tab栏切换功能

目标: 完成评论列表中的tab栏切换功能

步骤:

  1. 给tab栏注册点击事件
  2. 修改active进行切换

核心代码:

  • 给tab栏注册点击事件
<li
  className={item.type === this.state.active ? 'on' : ''}
  key={item.id}
  onClick={() => this.changeTab(item.type)}
>
  按{item.name}排序
</li>
  • 修改active属性
changeTab = (type) => {
  this.setState({
    active: type,
  })
}

删除评论功能

目标:完成评论列表案例的删除功能

步骤:

  1. 给删除按钮注册点击事件
  2. 通过setState删除对应的数据

核心代码

  • 给删除按钮注册点击事件
<span
  className="reply btn-hover"
  onClick={() => this.del(item.id)}
>
  删除
</span>
  • 通过setState删除对应的数据
del = (id) => {
  console.log(id)
  this.setState({
    list: this.state.list.filter((item) => item.id !== id),
  })
}

添加评论功能

目标: 完成评论列表添加功能

步骤:

  1. 通过受控组件的方式获取到评论内容
  2. 通过setState添加评论
  3. 重置评论的内容

核心代码

  • 受控组件方式获取内容
state = {
  // ...
  content: ''
}
​
<textarea
  cols="80"
  rows="5"
  placeholder="发条友善的评论"
  className="ipt-txt"
  value={this.state.content}
  onChange={this.handleChange}
/>
​
handleChange = (e) => {
  this.setState({
    content: e.target.value,
  })
}
  • 点击按钮添加评论
<button className="comment-submit" onClick={this.add}>
  发表评论
</button>
​
add = () => {
  const newComment = {
    id: Date.now(),
    author: '作者',
    comment: this.state.content,
    time: new Date(),
    // 1: 点赞 0:无态度 -1:踩
    attitude: 0,
  }
  this.setState({
    list: [newComment, ...this.state.list],
    content: '',
  })
}

点赞与踩的功能

目标: 完成点赞与踩的功能

步骤:

  1. 注册点击事件
  2. 修改点赞状态

核心代码

  • 注册点击事件
<span
  onClick={() =>
    this.changeAttitude(
      item.id,
      item.attitude === 1 ? 0 : 1
    )
  }
  className={[
    'like',
    item.attitude === 1 ? 'liked' : '',
  ].join(' ')}
>
  <i className="icon" />
</span>
<span
  onClick={() =>
    this.changeAttitude(
      item.id,
      item.attitude === -1 ? 0 : -1
    )
  }
  className={[
    'hate',
    item.attitude === -1 ? 'hated' : '',
  ].join(' ')}
>
  <i className="icon" />
</span>
  • 修改点赞状态
changeAttitude = (id, attitude) => {
  this.setState({
    list: this.state.list.map((item) => {
      if (item.id === id) {
        return {
          ...item,
          attitude,
        }
      } else {
        return item
      }
    }),
  })
}

我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿