基本使用与理解、组件实例的三大核心属性与事件处理
注意:当前 react 版本为 16.8.0
一、基本理解与使用
1、函数式组件
/**
_ 执行了 ReactDOM.render(, … 之后,发生了什么?
_ 1.React 解析组件标签,找到了 MyComponent 组件
_ 2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟 DOM 转为真实 DOM,随后呈现在页面中
_/
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>函数式组件</title>
</head>
<body>
<div id="test"></div>
<!-- 引入 react 核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入 react-dom,用于支持 react 操作DOM -->
<script
type="text/javascript"
src="../js/react-dom.development.js"
></script>
<!-- 引入babel, 用于将 jsx 转为 js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!-- 此处一定要写babel -->
<script type="text/babel">
// 1、创建函数式组件
function MyComponent() {
console.log(this) // babel 开启严格模式,将自定义的 this 不再是window,而是undefined
return <h2>我是用函数定义的组件(适用于【简单组件】)的定义</h2>
}
// 2、渲染组件到页面
ReactDOM.render(<MyComponent />, document.getElementById('test'))
/**
* 执行了 ReactDOM.render(<MyComponent/>, .... 之后,发生了什么?
* 1.React 解析组件标签,找到了 MyComponent 组件
* 2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟 DOM 转为真实 DOM,随后呈现在页面中
*/
</script>
</body>
</html>
2、类的复习
总结:
1、类中的构造器不是必须要写的,要对实例进行一些初始化的操作,如添加指定属性时才写
2、如果 A 类继承了 B 类且 A 类中写了构造器,那么 A 类构造器中 super 时必须要调用的
3、类中所定义的方法,都是放在了类的原型对象上,供实例区使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script type="text/javascript">
// 创建一个类
class Person {
// 构造器方法
constructor(name, age) {
// 构造器中的this是谁?—— 类的实力对象
this.name = name
this.age = age
}
// 一般方法:除了构造器以外的自定义方法
speak() {
// speak 方法放在了哪里?——类的原型对象上,供实例使用
// 通过 Person 实例调用 speak 时,speak 中的 this 就是 Person 实例
console.log(`我叫${this.name},今年${this.age} 岁`)
}
// 原型链:当查找一个不存在于自身的原型时,会查找原型,一层一层找下去,直到找到 Object,这就是一个原型链
}
// 创建一个实例对象
let p1 = new Person('tom', 20)
console.log(p1)
p1.speak()
// 创建一个 student 类继承于Person类
class student extends Person {
constructor(name, age, grade) {
super(name, age)
this.grade = grade
}
// 重写从父类继承过来的方法
speak() {
console.log(
`我叫${this.name},今年${this.age} 岁,我正在读${this.grade}`,
)
}
study() {
// speak 方法放在了哪里?——类的原型对象上,供实例使用
// 通过 student 实例调用 speak 时,speak 中的 this 就是 student 实例
console.log('我学习很用心')
}
}
let s1 = new student('小张', 15, '九年级')
console.log(s1)
s1.speak()
s1.study()
</script>
</body>
</html>
3、类式组件
_ 执行了 ReactDOM.render(, … 之后,发生了什么?
_ 1.React 解析组件标签,找到了 MyComponent 组件
_ 2.发现组件是使用类定义的,随后 new 出来该类的实例,并通过该实例调用到 原型上的 render 方法。
_ 3.将 render 返回的虚拟 DOM 转为真实 DOM,随后呈现在页面中
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>类式组件</title>
</head>
<body>
<div id="test"></div>
<!-- 引入 react 核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入 react-dom,用于支持 react 操作DOM -->
<script
type="text/javascript"
src="../js/react-dom.development.js"
></script>
<!-- 引入babel, 用于将 jsx 转为 js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!-- 此处一定要写babel -->
<script type="text/babel">
// 1、创建类式组件
class MyComponent extends React.Component {
render() {
// render 是放在哪里的?—— MyComponent 的原型对象上,供实例使用
// render 中的this 是谁?—— MyComponent 的实例对象。MyComponent 组件的实例对象
return <h2>我是用类定义的组件(适用于【复杂组件】)的定义</h2>
}
}
// 2、渲染组件到页面
ReactDOM.render(<MyComponent />, document.getElementById('test'))
/**
* 执行了 ReactDOM.render(<MyComponent/>, .... 之后,发生了什么?
* 1.React 解析组件标签,找到了 MyComponent 组件
* 2.发现组件是使用类定义的,随后new 出来该类的实例,并通过该实例调用到 原型上的 render 方法。
* 3.将render 返回的虚拟 DOM 转为真实 DOM,随后呈现在页面中
*/
</script>
</body>
</html>
4、简单组件和复杂组件
简单组件:没有状态
复杂组件:有状态(state)的组件
例子:
人 状态 影响 行为
组件 状态 驱动 数据
二、组件实例的三大核心属性 1:state
1、例子,点击文字切换 凉爽和炎热
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>切换天气</title>
</head>
<body>
<div id="test"></div>
<!-- 引入 react 核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入 react-dom,用于支持 react 操作DOM -->
<script
type="text/javascript"
src="../js/react-dom.development.js"
></script>
<!-- 引入babel, 用于将 jsx 转为 js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!-- 此处一定要写babel -->
<script type="text/babel">
// 1、创建组件
class Weather extends React.Component {
// 构造器调用几次?—— 1次
constructor(props) {
console.log('constructor')
super(props)
// 借用构造器初始化 状态
this.state = {
isHot: false,
wind: '微风',
}
// bind 改变 this 的指向并且返回一个新函数
this.changeWeather = this.changeWeather.bind(this) // 解决类中方法局部严格模式导致 this 为 undefined 问题
}
// render调用几次?—— 1 + n 次, 1 是初始化的那次, n 是状态更新的次数
render() {
console.log('render')
// 读取状态
let { isHot, wind } = this.state
console.log(this, this.state)
return (
<h1 onClick={this.changeWeather}>
今天天气很{isHot ? '炎热' : '凉爽'}, {wind}
</h1>
)
}
// changeWeather调用几次?—— 点几次调几次
changeWeather() {
// changeWeather 放在那里?—— Weather 的原型对象,供实例使用
// 由于changeWeather 是作为 onClick 的回调,所以不是通过实例调用的,是直接调用
// 类中的方法 默认开启了局部的严格模式,所以 changeWeather 中的 this 是 undefined
// console.log('点击文字', this) // 为什么this 是 undefined?——类中的方法 默认开启了局部的严格模式,所以 changeWeather 中的 this 是 undefined
// 获取原来的 isHot 的值
let { isHot } = this.state
// 严重注意:状态(State)里面的数据不能直接更改, 要借助一个内置的 API 去更改
// this.state.isHot = !isHot // 这行就是直接更改!这是错误写法
// 严重注意: 状态(state)必须通过 setState 进行更改,且更新是一种合并,不是替换
this.setState({ isHot: !isHot })
console.log(isHot, this)
}
}
// 2、渲染组件到页面
// ReactDOM.render(组件,容器)
ReactDOM.render(<Weather />, document.getElementById('test'))
</script>
</body>
</html>
为什么要写构造器?—— 为了初始化状态和改变 this 的指向
setState 更改数据时候是合并还是替换?—— 是合并,不是替换
constructor 构造器调用几次?—— 1 次
render 调用几次?—— 1 + n 次, 1 是初始化的那次, n 是状态更新的次数
changeWeather 调用几次?—— 点几次调几次
类中的方法 默认开启了局部的严格模式, this 为 undefined
onClick={changeTitle} :指定函数,点击的时候 react 帮你调用函数,不可以加小括号,如果添加了页面渲染完毕 react 就会直接调用函数
1.1 复习–原生事件绑定方式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>原生事件绑定</title>
</head>
<body>
<button id="btn1">按钮1</button>
<button id="btn2">按钮2</button>
<button onclick="demo()">按钮3</button>
<script type="text/javascript">
let btn1 = document.getElementById('btn1')
btn1.addEventListener(
'click',
() => {
alert('按钮1 被点击了')
},
false,
)
let btn2 = document.getElementById('btn2')
btn2.onclick = () => {
alert('按钮2 被点击了')
}
function demo() {
alert('按钮3 被点击了')
}
</script>
</body>
</html>
1.2 复习–类中的方法 this 指向
类中所定义的方法 局部都开启了严格模式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>3_类中的方法this 指向.html</title>
</head>
<body>
<script type="text/javascript">
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
// 类中所定义的方法 局部都开启了严格模式
speak() {
// speak 方法放在了哪里?—— 类的原型对象上,供实例使用
// 通用 Person 实例调用 speak 时,speak 中的 this 就是 Person 实例
console.log(this)
}
}
let p1 = new Person('Tom', 20)
p1.speak() // 通过实例调用 speak 方法
const x = p1.speak // 函数的直接调用
x() // undefined : 类中所定义的方法 局部都开启了严格模式,直接调用 this 是 undefined
</script>
</body>
</html>
1.3 类的复习—类中添加属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script type="text/javascript">
class Car {
constructor(name, price) {
this.name = name
this.price = price
}
// 类中可以直接写赋值语句,如下的代码的含义是: 给Car 的实例对象添加一个属性,名为 wheel,值为 4
wheel = 4
}
let c1 = new Car('奔驰C63', 199)
let c2 = new Car('奔驰111C63', 100)
console.log(c2)
console.log(c1)
</script>
</body>
</html>
1.3 例子的简写。state 的简写
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>state 的简写</title>
</head>
<body>
<div id="test"></div>
<!-- 引入 react 核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入 react-dom,用于支持 react 操作DOM -->
<script
type="text/javascript"
src="../js/react-dom.development.js"
></script>
<!-- 引入babel, 用于将 jsx 转为 js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!-- 此处一定要写babel -->
<script type="text/babel">
// 1、创建组件
class Weather extends React.Component {
state = {
isHot: false,
wind: '微风',
}
render() {
// console.log(this)
let { isHot, wind } = this.state
return (
<h1 onClick={this.changeWeather}>
今天天气很{isHot ? '炎热' : '凉爽'}, {wind}
</h1>
)
}
// 以下代码相当于 changeWeather = this.changeWeather.bind(this)
// 此处不要写普通函数。需要写箭头函数:因为箭头函数没有自己的this,会找外层的 this
changeWeather = () => {
console.log(this)
let { isHot } = this.state
this.setState({ isHot: !isHot })
}
}
// 2、渲染组件到页面
ReactDOM.render(<Weather />, document.getElementById('test'))
</script>
</body>
</html>
2、理解
1)state 是组件对象最重要的属性,值是对象(可以包含多个 key-value 的组合)—— 因为 setState 更改状态的时候接收的是 对象
2)组件被称为“状态机”, 通过更新组件的 state 来更新对应的页面显示(重新渲染组件)
3、强烈注意
1)组件中 render 方法中的 this 为组件实例对象
2)组件自定义的方法中的 this 为 undefined,如何解决?
a、强制绑定 this:通过函数对象的 bind()
b、箭头函数
3)状态数据:不能直接修改或更新
三、组件实例的三大核心属性 2:props
1、例子:自定义用来显示一个人员信息的组件
1.1 要求:
1)姓名必须指定,且为字符串类型
2)性别为字符串类型,如果性别没有指定,默认为男
3)年龄必须指定,且为数字类型
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>props基本使用</title>
</head>
<body>
<div id="test"></div>
<div id="test1"></div>
<div id="test2"></div>
<!-- 引入 react 核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入 react-dom,用于支持 react 操作DOM -->
<script
type="text/javascript"
src="../js/react-dom.development.js"
></script>
<!-- 引入babel, 用于将 jsx 转为 js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!-- 此处一定要写babel -->
<script type="text/babel">
// 1、创建组件
class Person extends React.Component {
render() {
console.log(this)
let { name, gender, age } = this.props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{gender}</li>
<li>年龄:{age}</li>
</ul>
)
}
}
// 2、渲染组件到页面
// ReactDOM.render(组件,容器)
const p = { name: 'Tom54', gender: '男', age: 20 }
// {...p} 一般来说展开运算符不能展开对象,但是 在 babel 和react 中可以用来展开对象作批量属性到组件上
ReactDOM.render(<Person {...p} />, document.getElementById('test'))
ReactDOM.render(
<Person name="jerry" gender="女" age={18} />,
document.getElementById('test1'),
)
ReactDOM.render(
<Person name="jack" gender="男" age={5} />,
document.getElementById('test2'),
)
</script>
</body>
</html>
1.2 复习–展开运算符(…)
用法:
1)展开一个数组
2)连接数组
3)在函数中使用
4)字面量的形式复制一个对象,是深克隆
5)合并对象(复制对象的同时修改属性)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script type="text/javascript">
let arr = [1, 2, 3, 4, 5]
console.log(...arr) // 1、展开一个数组
let arr2 = [11, 12, 14, 16]
let arr3 = [...arr, ...arr2] // 2、连接数组
// 3、在函数中使用
function sum(...numbers) {
console.log('@', numbers)
// 实现方式1:
// let total = 0
// numbers.forEach((el) => {
// total += el
// })
// return total
//实现求和方法2
return numbers.reduce((preValue, currentValue) => {
return preValue + currentValue
})
}
console.log(sum(1, 2, 5)) //
let p = { name: 'Tom', page: 15 }
let p2 = { ...p } // 4、字面量的形式复制一个对象,是深克隆
// console.log(...p) // 报错,展开运算符不能展开对象
p.name = 'jack'
console.log(p2.name)
//5、合并对象(复制对象的同时修改属性)
let p3 = { ...p, name: 'Jerry' }
console.log(p3.name)
</script>
</body>
</html>
1.3 对传递的属性(props)进行校验
React 15.x 之前 React.PropTypes 有维护
React 16.x 后 React.PropTypes 被废弃
<!-- 引入 prop-types,用于对组件标签属性进行限制, 存在 PropTypes -->
<script type="text/javascript" src="../js/prop-types.js"></script>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>props基本使用</title>
</head>
<body>
<div id="test"></div>
<div id="test1"></div>
<div id="test2"></div>
<!-- 引入 react 核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入 react-dom,用于支持 react 操作DOM -->
<script
type="text/javascript"
src="../js/react-dom.development.js"
></script>
<!-- 引入babel, 用于将 jsx 转为 js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!-- 引入 prop-types,用于对组件标签属性进行限制, 存在 PropTypes -->
<script type="text/javascript" src="../js/prop-types.js"></script>
<!-- 此处一定要写babel -->
<script type="text/babel">
// 1、创建组件
class Person extends React.Component {
render() {
let { name, gender, age } = this.props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{gender}</li>
<li>年龄:{age + 1}</li>
</ul>
)
}
}
// react 识别propTypes属性就是加规则
// 对标签属性进行类型、必要性的限制
Person.propTypes = {
name: PropTypes.string.isRequired, // 姓名必须指定,且为字符串类型
gender: PropTypes.string, // 性别为字符串类型
age: PropTypes.number, //年龄为数字类型
speak: PropTypes.func, // 限制 speak 为函数
}
// 指定默认标签属性值
Person.defaultProps = {
gender: '男',
age: 18,
}
// 2、渲染组件到页面
// ReactDOM.render(组件,容器)
const p = { name: 'Tom54', age: 20 }
ReactDOM.render(<Person {...p} />, document.getElementById('test'))
ReactDOM.render(
<Person name={12} gender="女" age={18} speak="1" />,
document.getElementById('test1'),
)
ReactDOM.render(
<Person name="jack" gender="男" />,
document.getElementById('test2'),
)
function speak() {
console.log('说话了')
}
</script>
</body>
</html>
1.4 复习-- 类的关键字 static
class Car {
constructor(name, price) {
this.name = name
this.price = price
}
// 类中可以直接写赋值语句,如下的代码的含义是: 给Car 的实例对象添加一个属性,名为 wheel,值为 4
wheel = 4
a = 1
static demo = 100 // 添加一个属性给 类
}
let c1 = new Car('奔驰C63', 199)
console.log(c2)
console.log(c2.demo())
1.5 props 简写方式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>props基本使用</title>
</head>
<body>
<div id="test"></div>
<div id="test1"></div>
<div id="test2"></div>
<!-- 引入 react 核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入 react-dom,用于支持 react 操作DOM -->
<script
type="text/javascript"
src="../js/react-dom.development.js"
></script>
<!-- 引入babel, 用于将 jsx 转为 js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!-- 引入 prop-types,用于对组件标签属性进行限制, 存在 PropTypes -->
<script type="text/javascript" src="../js/prop-types.js"></script>
<!-- 此处一定要写babel -->
<script type="text/babel">
// 1、创建组件
class Person extends React.Component {
render() {
let { name, gender, age } = this.props
// this.props.name = '哈哈哈'// 此行代码会报错,因为 props 是只读的
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{gender}</li>
<li>年龄:{age + 1}</li>
</ul>
)
}
// 对标签属性进行类型、必要性的限制
static propTypes = {
name: PropTypes.string.isRequired, // 姓名必须指定,且为字符串类型
gender: PropTypes.string, // 性别为字符串类型
age: PropTypes.number, //年龄为数字类型
speak: PropTypes.func, // 限制 speak 为函数
}
// 指定默认标签属性值
static defaultProps = {
gender: '男',
age: 18,
}
}
// 2、渲染组件到页面
// ReactDOM.render(组件,容器)
const p = { name: 'Tom54', age: 20 }
ReactDOM.render(<Person {...p} />, document.getElementById('test'))
ReactDOM.render(
<Person name={12} gender="女" age={18} speak={speak} />,
document.getElementById('test1'),
)
ReactDOM.render(
<Person name="jack" gender="男" />,
document.getElementById('test2'),
)
function speak() {
console.log('说话了')
}
</script>
</body>
</html>
构造器 constructor
在 React 组件挂载之前,会调用它的构造函数。在为 React.Component 子类实现构造函数时,应在其他语句之前调用 super(props), 否则, this.props 在构造函数中可能会出现未定义的 bug
构造器是否接收 props,是否传递给 super, 取决于:是否希望在构造器中通过 this 访问 props (几乎不用)
1.6 函数式组件使用 props。三大属性中函数式组件仅能使用 props
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>简写</title>
</head>
<body>
<div id="test"></div>
<!-- 引入 react 核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入 react-dom,用于支持 react 操作DOM -->
<script
type="text/javascript"
src="../js/react-dom.development.js"
></script>
<!-- 引入babel, 用于将 jsx 转为 js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!-- 引入 prop-types,用于对组件标签属性进行限制, 存在 PropTypes -->
<script type="text/javascript" src="../js/prop-types.js"></script>
<!-- 此处一定要写babel -->
<script type="text/babel">
// 1、创建组件
function Person(props) {
let { name, gender, age } = props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{gender}</li>
<li>年龄:{age + 1}</li>
</ul>
)
}
Person.propTypes = {
name: PropTypes.string.isRequired, // 姓名必须指定,且为字符串类型
gender: PropTypes.string, // 性别为字符串类型
age: PropTypes.number, //年龄为数字类型
}
Person.defaultProps = {
gender: '男',
age: 18,
}
// 2、渲染组件到页面
// ReactDOM.render(组件,容器)
const p = { name: 'Tom', age: 15, gender: '女' }
ReactDOM.render(<Person {...p} />, document.getElementById('test'))
</script>
</body>
</html>
2、理解
1)每个组件对象都会有 props(properties 的简写)属性
2)组件标签的所有属性都保存在 props 中
3、作用
1)通过标签属性从组件外向组件内传递变化的数据
2)注意:组件内部不要修改 props 数据
4、编码操作
1)内部读取某个属性值
this.props.name
2)对 props 中的属性值进行类型限制和必要性限制
第一种方式 (React v15.5 开始已经弃用)
Person.propTypes = {
name: React.PropTyes.string,
}
第二种方式 (新)
使用 prop-types 库进行限制(需要引入 prop-types 库)
Person.propTypes = {
name: React.PropTyes.string,
}
3)扩展属性:将对象的所有属性通过 props 传递
<Person {...param} />
4)默认属性值
Person.defaultProps = {
gender: '男',
age: 18,
}
5)组件类的构造函数(项目中基本不用)
constructor(props) {
super(props)
console.log(props) // 打印所有属性
}
四、组件实例的三大核心属性 3:refs 与事件处理
1、例子
1.1 需求:自定义组件,功能说明如下:
1)点击按钮,提示第一个输入框中的值
2)当第 2 个输入框失去焦点时,提示这个输入框中的值
1.2 过时 API :String 类型的 Refs
string 类型的 ref 存在一些效率问题。已过时会在未来的版本被移除
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>refs 与事件处理</title>
</head>
<body>
<div id="test"></div>
<!-- 引入 react 核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入 react-dom,用于支持 react 操作DOM -->
<script
type="text/javascript"
src="../js/react-dom.development.js"
></script>
<!-- 引入babel, 用于将 jsx 转为 js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!-- 引入 prop-types,用于对组件标签属性进行限制, 存在 PropTypes -->
<script type="text/javascript" src="../js/prop-types.js"></script>
<!-- 此处一定要写babel -->
<script type="text/babel">
// 1、创建组件
class MyComponent extends React.Component {
render() {
return (
<div>
<input ref="input1" type="text" placeholder="点击按钮提示数据" />
<button onClick={this.showData}>点我提示左侧的数据</button>
<input
ref="input2"
type="text"
placeholder="失去焦点提示数据"
onBlur={this.blurInput}
/>
</div>
)
}
// 展示左侧输入框的数据
showData = () => {
console.log('点击按钮提示数据', this.refs.input1.value)
}
blurInput = () => {
console.log('失去焦点提示数据:', this.refs.input2.value)
}
}
// 2、渲染组件到页面
// ReactDOM.render(组件,容器)
ReactDOM.render(<MyComponent />, document.getElementById('test'))
</script>
</body>
</html>
1.3 回调函数形式的 ref
如果 ref 回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数 null, 然后第二次会传输参数 DOM 元素。
这是因为在每次渲染时创建一个新的函数实例,所以 React 清空旧的 ref 并且设置新的。
通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题,但是大多数情况下它是无关紧要的
内联函数方式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>refs 与事件处理</title>
</head>
<body>
<div id="test"></div>
<!-- 引入 react 核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入 react-dom,用于支持 react 操作DOM -->
<script
type="text/javascript"
src="../js/react-dom.development.js"
></script>
<!-- 引入babel, 用于将 jsx 转为 js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!-- 引入 prop-types,用于对组件标签属性进行限制, 存在 PropTypes -->
<script type="text/javascript" src="../js/prop-types.js"></script>
<!-- 此处一定要写babel -->
<script type="text/babel">
// 1、创建组件
class MyComponent extends React.Component {
render() {
return (
<div>
<input
ref={(currentNode) => (this.input1 = currentNode)}
type="text"
placeholder="点击按钮提示数据"
/>
<button onClick={this.showData}>点我提示左侧的数据</button>
<input
ref={(currentNode) => (this.input2 = currentNode)}
type="text"
placeholder="失去焦点提示数据"
onBlur={this.blurInput}
/>
</div>
)
}
// 展示左侧输入框的数据
showData = () => {
let { input1 } = this
console.log(input1.value)
}
blurInput = () => {
let { input2 } = this
console.log(input2.value)
}
}
// 2、渲染组件到页面
// ReactDOM.render(组件,容器)
ReactDOM.render(<MyComponent />, document.getElementById('test'))
</script>
</body>
</html>
回调函数的 ref 中 回调函数执行次数?—— 更新(触发 render 时)过程中会被执行两次,第一次传入参数 null, 然后第二次会传输参数 DOM 元素
jsx 中注释代码{//}**
通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题,但是大多数情况下它是无关紧要的
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>refs 与事件处理</title>
</head>
<body>
<div id="test"></div>
<!-- 引入 react 核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入 react-dom,用于支持 react 操作DOM -->
<script
type="text/javascript"
src="../js/react-dom.development.js"
></script>
<!-- 引入babel, 用于将 jsx 转为 js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!-- 引入 prop-types,用于对组件标签属性进行限制, 存在 PropTypes -->
<script type="text/javascript" src="../js/prop-types.js"></script>
<!-- 此处一定要写babel -->
<script type="text/babel">
// 1、创建组件
class MyComponent extends React.Component {
state = {
isHot: true,
}
render() {
let { isHot } = this.state
return (
<div>
<h2>今天天气很{isHot ? '炎热' : '凉爽'}</h2>
{/*<input
ref={(currentNode) => {
this.input1 = currentNode
console.log('@@', currentNode)
}}
type="text"
placeholder="点击按钮提示数据"
/>*/}
<input
ref={this.saveInput}
type="text"
placeholder="点击按钮提示数据"
/>
<button onClick={this.showData}>点我提示数据</button>
<button onClick={this.changeWeather}>点我切换天气</button>
</div>
)
}
saveInput = (currentNode) => {
this.input1 = currentNode
console.log(this)
}
// 展示左侧输入框的数据
showData = () => {
let { input1 } = this
console.log(input1.value)
}
// 切换天气
changeWeather = () => {
let { isHot } = this.state
this.setState({ isHot: !isHot })
}
}
// 2、渲染组件到页面
// ReactDOM.render(组件,容器)
ReactDOM.render(<MyComponent />, document.getElementById('test'))
</script>
</body>
</html>
1.4 createRef (官方推荐写法)
React.createRef 调用后可以返回一个容器,该容器可以存储被 ref 所表示的节点,该容器是 “专人专用” 的(只能存放一个 ref),后面的会覆盖前面的
缺点:每一个 ref 都需要创建容器
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>createRef</title>
</head>
<body>
<div id="test"></div>
<!-- 引入 react 核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入 react-dom,用于支持 react 操作DOM -->
<script
type="text/javascript"
src="../js/react-dom.development.js"
></script>
<!-- 引入babel, 用于将 jsx 转为 js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!-- 引入 prop-types,用于对组件标签属性进行限制, 存在 PropTypes -->
<script type="text/javascript" src="../js/prop-types.js"></script>
<!-- 此处一定要写babel -->
<script type="text/babel">
// 1、创建组件
class MyComponent extends React.Component {
/**
* React.createRef调用后可以返回一个容器,该容器可以存储被 ref 所表示的节点
*/
myRef = React.createRef()
myRef2 = React.createRef()
render() {
return (
<div>
<input
ref={this.myRef}
type="text"
placeholder="点击按钮提示数据"
/>
<button ref={this.myRef} onClick={this.showData}>
点我提示数据
</button>
<input
ref={this.myRef2}
type="text"
onBlur={this.showData2}
placeholder="失去焦点提示数据"
/>
</div>
)
}
// 展示左侧输入框的数据
showData = () => {
let { current } = this.myRef
console.log(current.value)
}
showData2 = () => {
let { current } = this.myRef2
console.log(current.value)
}
}
// 2、渲染组件到页面
// ReactDOM.render(组件,容器)
ReactDOM.render(<MyComponent />, document.getElementById('test'))
</script>
</body>
</html>
2、理解
组件内的标签可以定义 ref 属性来标识自己
3、编码
1)字符串形式的 ref
<input ref="input2" type="text" />
2)回调形式的 ref
<input
ref={(currentNode) => {
this.input1 = currentNode
}}
/>
3)createRef 创建 ref 容器
myRef = React.createRef()
<input
ref={this.myRef}
type="text"
/>
需要避免过度使用 ref
4、事件处理
1)通过 onXxx 属性指定事件处理函数(注意大小写)
a、React 使用的是自定义(合成)事件,而不是使用的原生 DOM 事件 —— 为了更好的兼容性
b、React 中的事件是通过事件委托方式处理的(委托给组件最外层的元素)—— 为了高效
事件委托的原理是事件冒泡
2)通过 event.target 得到发生事件的 DOM 元素对象 —— 不要过度使用 ref
当发生事件的元素就是操作的元素,就可以省略 ref , 使用 event.target 获取即可
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>createRef</title>
</head>
<body>
<div id="test"></div>
<!-- 引入 react 核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入 react-dom,用于支持 react 操作DOM -->
<script
type="text/javascript"
src="../js/react-dom.development.js"
></script>
<!-- 引入babel, 用于将 jsx 转为 js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!-- 引入 prop-types,用于对组件标签属性进行限制, 存在 PropTypes -->
<script type="text/javascript" src="../js/prop-types.js"></script>
<!-- 此处一定要写babel -->
<script type="text/babel">
// 1、创建组件
class MyComponent extends React.Component {
/*
1)通过 onXxx 属性指定事件处理函数(注意大小写)
a、React 使用的是自定义(合成)事件,而不是使用的原生 DOM 事件
b、React 中的事件是通过事件委托方式处理的(委托给组件最外层的元素)
2)通过 event.target 得到发生事件的 DOM 元素对象
*/
// 创建 ref 容器
myRef = React.createRef()
myRef2 = React.createRef()
render() {
return (
<div>
<input
ref={this.myRef}
type="text"
placeholder="点击按钮提示数据"
/>
<button ref={this.myRef} onClick={this.showData}>
点我提示数据
</button>
<input
type="text"
onBlur={this.showData2}
placeholder="失去焦点提示数据"
/>
</div>
)
}
// 展示左侧输入框的数据
showData = () => {
let { current } = this.myRef
console.log(current.value)
}
showData2 = (event) => {
console.log(event.target.value)
}
}
// 2、渲染组件到页面
// ReactDOM.render(组件,容器)
ReactDOM.render(<MyComponent />, document.getElementById('test'))
</script>
</body>
</html>