文章目录
1. 事件处理
React事件处理与DOM事件处理的区别:
1、在语法上的不同点:
- React中事件的命名采用驼峰命名法。
- 响应事件的函数要以对象的形式赋值。
DOM方式
<button οnclick=“clickMe”>点我</button>
React方式
<button onClick={clickButton}>不服点我</button>
//clickButton是一个函数
2、在阻止事件的默认行为有区别: React事件是合成的,DOM事件是原生的
- DOM:返回false阻止默认事件
- React:显示调用事件对象
e.preventDefault
来阻止事件的默认行为。
在React中获取虚拟组件中的标签的值:
- 使用组件的refs属性
- 在虚拟组件的标签中定义事件,在事件中通过箭头函数获取标签的值
- 使用事件对象:event
1.1 事件处理函数
1.1.1 使用ES6的箭头函数
class MyComponent extends React.Component{
constructor(props){
super(props);
this.state={
number:0
}
handleClick=()=>{
++this.state.number;
console.log(this.state.number);
}
render(){
return(
<div>
<button type='button' onClick={this.handleClick}>点我</button>
</div>
)
}
}
ReactDOM.render(<MyComponent/>,document.getElementById('example'));
当前this指向的是当前组件的实例。
不足:当组件的逻辑比较复杂时,把组件的逻辑写在{ }内会导致render()函数变得臃肿,不能比较直观的看出ui结构,代码可读性不好。
1.1.2 在组件中定义事件处理函数
class MyComponent extends React.Component{
constructor(props){
super(props);
this.state={
number:0
}
this.handleClick=this.handleClick.bind(this);
}
handleClick(){
++this.state.number;
console.log(this.state.number);
}
render(){
return(
<div>
<button type='button' onClick={this.handleClick}>点我</button>
</div>
)
}
}
ReactDOM.render(<MyComponent/>,document.getElementById('example'));
这种方法的好处是每次render渲染都不会重新创建一个回调函数,没有额外的性能损失。
但是如果在一个组件中有很多的事件函数时这种在构造函数中绑定this的方法会显得繁琐.
1.1.3 在给事件赋值时绑定this
class MyComponent extends React.Component{
constructor(props){
super(props);
this.state={
number:0
}
}
handleClick(){
++this.state.number;
console.log(this.state.number);
}
render(){
return(
<div>
<button type='button' onClick={this.handleClick.bind(this)}>点我</button>
</div>
)
}
}
ReactDOM.render(<MyComponent/>,document.getElementById('example'));
但是此方法在每次render时都会重新创建一个新的函数,性能有一定的损失。
但在事件处理函数需要传参数时,这种方法就比较好。
1.2 事件流
在该示例中,3个div嵌套显示,并且每个元素上均绑定onClick事件。当用户点击红色区域的div元素时,可以看到,控制台先后输出了child -> parent -> ancestor,这是因为在React的事件处理系统中,默认的事件流就是冒泡。
const style={
child:{
width:'100px',
height:'100px',
background:'red'
},
parent:{
width:'150px',
height:'150px',
background:'blue'
},
ancestor:{
width:'200px',
height:'200px',
background:'green'
}
}
class Example extends React.Component{
render(){
return(
<div onClick={()=> console.log('ancestor')} style={style.ancestor}>
<div onClick={ ()=> console.log('parent')} style={style.parent}>
<div onClick={ ()=> console.log('child')} style={style.child}></div>
</div>
</div>
)
}
}
ReactDOM.render(<Example/>,document.getElementById('example'));
- React默认的事件触发方式:冒泡方式
- 若将事件触发改为捕获方式:需要在事件名后带上
Capture
后缀
<div onClickCapture={()=> console.log('ancestor')} style={style.ancestor}>
<div onClickCapture={ ()=> console.log('parent')} style={style.parent}>
<div onClickCapture={ ()=> console.log('child')} style={style.child}></div>
</div>
</div>
1.3 事件委托
在合成事件系统中,所有的事件都是绑定在document元素上。即:虽然我们在某个react元素上绑定了事件,但是,最后事件都委托给document统一触发。
在合成事件中只能阻止合成事件中的事件传播。
eg:点击红色div,可以看到控制台上只输出了child,说明这时已经成功阻止了冒泡。
<div id='example'></div>
<script type="text/babel">
const style={
child:{
width:'100px',
height:'100px',
background:'red'
},
parent:{
width:'150px',
height:'150px',
background:'blue'
},
ancestor:{
width:'200px',
height:'200px',
background:'green'
}
}
class Example extends React.Component{
render(){
return(
<div onClick={()=> console.log('ancestor')} style={style.ancestor}>
<div onClick={ ()=> console.log('parent')} style={style.parent}>
<div onClick={ (e)=> {
console.log('child');
e.stopPropagation();
}} style={style.child}></div>
</div>
</div>
)
}
}
ReactDOM.render(<Example/>,document.getElementById('example'));
</script>
执行流程如下:
React 阻止的事件流,并没有阻止真正DOM元素的事件触发,还是按照冒泡的方式,层层将事件交给上级元素进行处理,最后事件传播到docuement,触发合成事件。
在合成事件中,child触发时,e.stopPropagation()
被调用,合成事件中的事件被终止。
因此,合成事件中的stopPropagation无法阻止事件在真正元素上的传递,它只阻止合成事件中的事件流。
相反,如果绑定一个真正的事件,那么,合成事件则会被终止。
1.4 事件对象
在合成事件中,依然可以获取到事件发生时的event对象。
eg:
<div id='example'></div>
<script type="text/babel">
const style = {
div:{
width:'200px',
height:'200px',
background:'red'
},
}
class App extends React.Component{
state = {
x: 0,
y: 0
}
render() {
return (
<div>
<div style={style.div}
onClick = {
e => {
this.setState({
x: e.clientX,
y: e.clientY
})
}
}
>
x: {this.state.x},y : {this.state.y}
</div>
</div>
)
}
}
ReactDOM.render(<App/>,document.getElementById('example'));
</script>
合成事件中的event对象,并不是原生的event,只是说,我们可以通过它获取到原生event对象上的某些属性。
比如以上示例中的clientX和clientY。
而且,对于这个event对象,在整个合成事件中,只有一个,被全局共享,也就是说,当这次事件调用完成之后,这个event对象会被清空,等待下一次的事件触发,因此,我们无法在异步的操作中获取到event。
2. 受控组件、非受控组件
2.1 受控组件
在HTML中,标签
<input>
、<textarea>
、<select>
的值的改变通常是根据用户输入进行更新。
在React中,可变状态通常保存在组件的状态属性中,并且只能使用setState()更新,而呈现表单的React组件也控制着在后续用户输入时该表单中发生的情况,以这种由React控制的输入表单元素而改变其值的方式,称为:“受控组件”。
eg:点击登录输出用户的输入的用户名和密码
import React,{Compontent} from 'react';
class Mydemo extends React.Component {
constructor(props) {
super(props);
this.state = {name: ''};
this.handleNameChange = this.handleNameChange.bind(this);
}
handleNameChange(event) {
this.setState({ name: event.target.value });
};
render() {
return (
<div>
<input type="text" value={this.state.name} onChange={this.handleNameChange}/>
<br/> name : {this.state.name}
</div>
);
}
}
export default Mydemo;
- name开始是空字符串
''
。 - 当键入
a
,并handleNameChange
获取a和调用setState
。然后,该输入被重新呈现为具有的值a。 - 当继续键入
b
,handleNameChange
获取ab并设置该状态的值。现在再次重新渲染输入value="ab"
。
这也意味着表单组件可以立即响应输入更改。
受控执行情况:
元素 | 属性 | 方法 | 方法回调中的新值 |
---|---|---|---|
<input type='text'/> | value=‘string’ | onChange | event.target.value |
<input type='checxbox'/> | checked={boolean} | onChange | event.target.checked |
<input type='radio'/> | checked={boolean} | onChange | event.target.checked |
<textarea> | value=‘string’ | onChange | event.target.value |
<select> | value=‘option value’ | onChange | event.target.value |
可见效果:
- 当注释
this.setState({value: event.target.value})
; 这行代码,文本框再次输入时,页面不会重新渲染,所产生效果即是文本框输入不了值,即文本框值的改变受到setState()
方法的控制,在未执行时,不重新渲染组件。
2.2 非受控组件
表单数据由DOM本身处理。即不受
setState()
的控制,与传统的HTML表单输入相似,input输入值即显示最新值(使用ref
从DOM获取表单值)
import React,{Compontent} from 'react';
class Mydemo extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(event) {
alert('A name was submitted: ' + this.input.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" ref={(input) => this.input = input} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
export default Mydemo;
受控和非受控元素都有其优点,根据具体情况选择