React 组件介绍
目标
了解 React 组件的意义
组件
对特定功能的封装,主要用来对UI进行拆分。
内容
结构 HTML
样式 CSS
逻辑 JS
特点
- 独立
- 可复用
- 可组合
分类
- 基础组件:指
input
、button
这种基础标签,以及antd封装过的通用UI组件 - 业务组件:由基础组件组合成的业务抽象化UI。例如: 包含了A公司所有部门信息的下拉框
- 区块组件:由基础组件组件和业务组件组合成的UI块
- 页面组件:展示给用户的最终页面,一般就是对应一个路由规则
React组件的两种创建方式
目标
了解 React 组件的两种创建方式
- 使用 JS 中的
函数
创建组件 - 使用 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'))
对比
- 类组件比较繁琐
- 函数式组件比较简便
目前市面上的项目中都有使用
函数式组件-使用函数创建组件
目标
掌握函数式组件的定义和使用方式
定义组件
使用 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)
类组件-用class创建组件
目标
掌握类组件的基本用法
定义格式
使用 ES6 的 class 创建的组件,叫做类(class)组件
格式
// import { Component } from 'react'
// class 类名 extends Component {
import React form 'react'
class 类名 extends React.Component {
// ...
render () {
return 本组件的UI结构
}
}
注意:
- 类名必须以大写字母开头
- extends是一个关键字,用来实现类之间的继承。类组件应该继承 React.Component 父类,从而使用父类中提供的方法或属性。
- 类组件必须提供 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'))
使用组件的方式与函数式组件一致:可以使用单标签和双标签
有状态组件和无状态组件
目标
理解状态的概念,理解有状态组件和无状态组件的概念
什么是状态
定义:是用来描述事物在某一时刻的形态
的数据。一般称为state。例如:9月23号时书的库存数量; 18岁时人的身高;
特点:
-
状态能被改变,改变了之后视图会有对应的变化
作用:
- 保存数据。例如:要循环生成一份歌曲列表,那要提前准备好歌曲数据吧
- 为后续更新视图打下基础。如果用户点击了操作,让歌单的内容+1了,视图会自动更新(这是由react库决定的)
有状态组件和无状态组件
有状态组件:能定义state的组件。类组件就是有状态组件。
无状态组件:不能定义state的组件。函数组件又叫做无状态组件
注意:2019年02月06日,rect 16.8中引入了 React Hooks,从而函数式组件也能定义自己的状态了
无状态组件的应用场景
- 组件本身就不需要有状态。例如:渲染一段固定的内容
- 组件本身就没有状态,可以从外部传入
小结
-
状态:是用来描述事物在
某一时刻的形态
的数据 -
状态的特点: 能被修改,改了之后对应的视图也能更新。
类组件的状态
目标
掌握react类组件中定义状态和渲染状态
定义状态
固定格式,使用 state = 对象
来做初始化
import React from "react";
export default class Hello extends React.Component {
// 这里的state就是状态
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>
</>
);
}
事件绑定
目标
能掌握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'))
注意:
- 事件名是小驼峰命名格式
- 在类中补充方法
- this.fn不要加括号:
onClick={ this.fn() }
先调用fn(),然后将fn的执行结果当做click事件的处理函数
别忘记了写this
事件对象
目标
掌握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是?
}
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()
总结:
- class的内部,开启了局部严格模式
use strict
,所以this不会指向windowundefined
onClick={this.fn}
中,this.fn的调用并不是通过类的实例调用的,所以值是undefined
事件处理-this指向解决方案
目标
掌握常见的this指向解决方案
有三种方式
解决事件处理程序中this指向问题主要有三种方式
- Function.prototype.bind()
- 箭头函数
- 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>
)
}
}
缺点:1,需要额外包裹一个箭头函数,影响性能, 结构不美观
方式2:使用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>
)
}
}
方式3:
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的转义,所以没有任何问题
总结
- 方式3是最方便的,也是以后用的最多的方式
组件的状态-修改状态
目标
理解setState的格式,会使用setState函数来修改状态
setState
语法:this.setState({ 要修改的部分数据 })
作用:
1. 修改 state 2. 更新 UI
**错误示例**
state = {
count: 0
};
this.setState({
count: this.state.count++
})
理解状态不可变
react核心理念之状态不可变
不要直接修改当前状态值,而是创建新的状态值。
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>
)
}
获取表单元素的值的两种思路
目标
能够了解获取表单元素的值的两种思路
问题导入
如何获取表单元素(例如输入框)?
初始代码
class App extends React.Component {
render() {
return (
<div>
<h1>如何获取input中的值</h1>
<p><input type="text" /></p>
<button>点击按钮</button>
</div>
)
}
}
思路
有两种思路:
- 直接找到表单元素进行dom操作 --> 非受控组件
- 将表单元素值与react的state绑定到一起,把用户的修改同步到state中。让组件受到react的控制--> 受控组件
非受控组件-ref
目标
借助于ref,使用原生DOM的方式来获取表单元素的值
ref的使用格式
步骤
- 导入方法。
import { createRef } from 'react'
- 调用createRef方法创建引用,假设名为refDom。
const refDom = createRef()
- refDom设置给表单元素的ref属性。
<input ref={refDom}/>
- 通过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)
受控组件的思路:
- 在state中定义状态
- 将state中的状态与表单元素的value值绑定到一起,进而通过state中的状态来控制表单元素的值
受控组件:value值受到了react控制的表单元素
基本步骤
有两个步骤:
- 在state中定义状态
- 对表单元素做两件事
- 设置value为上面定义的状态
- 绑定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>
)
}
}
注意
使用受控组件的方式处理表单元素后,状态的值就是表单元素的值。即:想要操作表单元素的值,只需要操作对应的状态即可
常见的受控组件
文本框,文本域,下拉框,复选框
要点
不同类型的表单元素进行受控处理时的格式是不同的:
-
文本框、文本域、下拉框:value属性 + onChange事件
-
复选框: checked属性 + onChange事件
-
单选按钮组: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'))