事件处理
一、无状态组件
无状态组件就是组件内部没有(或不需要)state,无状态组件也可以理解为展示组件,仅做展示用。
函数组件其实就是无状态组件。
<script src="../js/react.js"></script>
<script src="../js/react-dom.js"></script>
<script src="../js/babel.min.js"></script>
<script type="text/babel">
//函数组件(就是无状态组件) ,内部没有state
function Vie(props){
return (
<div>
<p>
姓名:{props.name}
</p>
<p>
年龄:{props.age}
</p>
<p>
评价:{props.desc}
</p>
</div>
)
}
let obj = {
name:'晓晓',
age:19,
desc:'努力刻苦又善良!'
}
ReactDOM.render(
<Vie {...obj}></Vie>,
document.getElementById('box')
)
</script>
就是这么简单!
二、有状态组件
有状态组件就是不但外部可以传入props,内部也可以有state。
有状态组件也可以理解为容器组件,用来容纳展示组件,在容器组件里处理数据的逻辑,把结果在展示组件里呈现。
//有状态组件(容器组件) 内部有state,还可以外部传入props
class Viv extends React.Component{
constructor(){
super();
this.state = {
desc:'个人信息'
}
}
render=()=>{
return (
<div>
<h2>{this.state.desc}</h2>
{/* 容纳展示组件 */}
<Vie {...obj}></Vie>
</div>
)
}
}
//无状态组件 内部没有state
function Vie(props){
return (
<div>
<p>
姓名:{props.name}
</p>
<p>
年龄:{props.age}
</p>
<p>
评价:{props.desc}
</p>
</div>
)
}
let obj = {
name:'晓晓',
age:19,
desc:'努力刻苦又善良!'
}
ReactDOM.render(
<Viv {...obj}></Viv>,
document.getElementById('box')
)
三、事件处理
3.1、特点
在此之前,我们先看看react事件的特点:
1、React 事件绑定属性的命名采用驼峰式写法,而不是小写。如:onClick。
<input type="button" onClick={fn}/>//此处onClick,驼峰命名法
2、如果采用 JSX 的语法你需要传入一个函数作为事件处理函数,而不是一个字符串(DOM 元素的写法)
<input type="button" onClick={fn}/>//此处不加引号
- 错误示例:
<input type="button" onClick="fn"/>
3、在 React 中另一个不同是你不能使用return false 的方式阻止默认行为, 你必须明确的使用 preventDefault。
change(ev){
ev.preventDefault()//不用return false
}
4、事件处理函数里的this是undefined(啊………),如果希望事件处理函数里的this是React组件对象本身,则需要用bind。
change(){
//事件处理函数
console.log('事件函数里的this:',this);//undefined
}
3.2、事件语法
3.2.1、格式
<JSX元素 onClick={this.实例方法|函数体} />
<input type="button" value="测试01" onClick={this.fn01} />
<input type="button" value="测试02" onClick={()=>{console.log("事件绑定箭头函数")}} />
3.2.2、事件处理函数里的this问题
我们已经知道,react事件函数里的this是undefined,那么怎么才能让事件函数里的this指向React组件对象本身
?
提供了以下几种方案,我将一一演示;
1)、使用bind
- 在构造函数里写
this.方法=this.方法.bind(this)
class Viv extends React.Component{
constructor(){
super();
//将此处的this绑定到change事件函数,bind()方法会返回
//一个新的函数对象(改变了this指向的change函数)
this.change2 = this.change.bind(this)
}
change(){
console.log('事件函数里的this:',this);
}
render=()=>{
return (
<div>
{/* 在绑定事件时绑定改变this指向后的change函数 */}
<input type="button" value="点击触发事件2"
onClick={this.change2}/>
</div>
)
}
}
注意:以上代码还可以写成:
class Viv extends React.Component{
constructor(){
super();
//将此处的this绑定到change事件函数,bind()方法会返回
//一个新的函数对象(改变了this指向的change函数)
this.change = this.change.bind(this)
}
change(){
console.log('事件函数里的this:',this);
}
render=()=>{
return (
<div>
<input type="button" value="点击触发事件2" onClick={this.change}/>
</div>
)
}
}
细心的朋友应该发现了,
this.change2 = this.change.bind(this)
还可以写成:
this.change = this.change.bind(this)
既然会返回一个新的函数对象,为何不直接用原函数接收呢?相当于重新赋值了。
控制台运行结果:
- 在事件绑定时写
onClick={this.方法.bind(this)}
class Viv extends React.Component{
constructor(){
super();
}
change(){
console.log('事件函数里的this:',this);
}
render=()=>{
return (
<div>
{/* 此处直接使用bind */}
<input type="button" value="点击触发事件4"
onClick={this.change.bind(this)}/>
</div>
)
}
}
2)、使用箭头函数
- 事件绑定时使用
onClick={()=>this.方法()}
class Viv extends React.Component{
constructor(){
super();
}
change(){
console.log('事件函数里的this:',this);
}
render=()=>{
return (
<div>
{/*
这里注意:箭头函数内部this.change()带括号的,
因为此处绑定事件函数是把外部的箭头函数作为
事件处理函数绑定的,当点击触发事件则会
执行内部的this.change()执行change函数.
*/}
<input type="button" value="点击触发事件5"
onClick={()=>this.change()}/>
</div>
)
}
}
- 定义方法时,直接使用
方法=()=>{箭头函数定义方法} //这个方法可能是最简单的
class Viv extends React.Component{
constructor(){
super();
}
change=()=>{
/*
其他地方都不需要改,只需要把绑定的
事件函数写成箭头函数的形式就ok
*/
console.log('事件函数里的this:',this);
}
render=()=>{
return (
<div>
<input type="button" value="点击触发事件6"
onClick={this.change}/>
</div>
)
}
}
3.3、事件对象
在学习原生js时我们就已经学习过事件对象,react里的事件对象是经过react处理过的。
那么如何获取呢?
1)、直接声明(没有其它参数的情况下)
class Viv extends React.Component{
constructor(){
super();
}
change=(ev)=>{
console.log('事件对象:',ev);
}
render=()=>{
return (
<div>
<input type="button" value="点击触发事件"
onClick={this.change}/>
</div>
)
}
}
打印事件对象结果:
注意:形参ev的名字任意,但最好是event缩写或首字母。
2)、箭头函数里直接声明(有其它参数的情况下)
class Viv extends React.Component{
constructor(){
super();
}
change(ev,str){
console.log('事件对象:',ev);
console.log('str:',str);
}
render=()=>{
return (
<div>
<input type="button" value="点击触发事件" onClick={(ev)=>this.change(ev,'hello')}/>
</div>
)
}
}
打印结果:
此处有个坑,看你跳不跳:(需要注意)
1,当执行以下代码时,onClick绑定的是外部的箭头函数,那么事件处理函数就是这个箭头函数,而不再是change函数了。
<input type="button" value="点击触发事件"
onClick={(ev)=>this.change(ev,'hello')}/>
2,所以,要获取事件对象其实是获取箭头函数的事件对象,即ev,当点击按钮时执行change函数,将ev和str作为实参传给change函数。
3,在change函数内部,接收并打印这两个传来的值。
给事件属性绑定的函数,永远只会有一个参数,该参数就是事件对象。
3.4、阻止浏览器的默认行为
只能用preventDefault,不能在事件函数里使用return false
四、组件的内容
组件的内容,使用 props.children
属性获取。
function Mve(props){
return (
<div>
<p>组件内容:{props.children}</p>
</div>
)
}
ReactDOM.render(
<Mve>我是组件的内容</Mve>,
document.getElementById('box')
)
五、refs
用来获取DOM的,和vue里的refs类似。
表示对组件真正实例(也就是html标签,也就是DOM对象)的引用,其实就是ReactDOM.render()返回的组件实例;ref可以写在html标签里,也可以写在组件(自定义标签里)。
但是,尽量不要操作DOM。
使用:
1)赋值为字符串(官方不推荐使用)
<input type="text" ref="username" />
this.refs.username.value
2)赋值为回调函数
当给 HTML 元素添加 ref 属性时,ref 回调接收了底层的 DOM 元素作为参数。
ref 回调会在componentDidMount 或 componentDidUpdate 这些生命周期回调之前执行。
//ref的值赋成回调函数时,回调的参数就是当前dom元素。
// callback refs 回调
<jsx ref={el => this.定义一个实例属性 = el} //el就是dom对象
this.定义一个实例属性 //后期用作访问jsx元素
//当组件挂载时,将 DOM el元素传递给 ref 的回调
//当组件卸载时,则会传递 null。
//ref 回调会在 componentDidMount 和 componentDidUpdate 生命周期之前调用
如:
<input type="text" ref={(currDom) => this.input1 = currDom} />
<input type="text" ref={(currDom) => this.input2 = currDom} />
3)React.createRef() (React16.3提供)
使用此方法来创建ref。将其赋值给一个变量,通过ref挂载在dom节点或组件上,该ref的current属性将能拿到dom节点或组件的实例
//1、在构造函数里
// 实例化
this.firstRef = React.createRef() //发生在构造器
//2、挂载在ref属性上
<jsx ref={this.firstRef} />
//3、获取dom原始
this.firstRef.current //current是官方的属性