React :元素构成组件,组件又构成应用。
React核心思想是组件化,其中 组件 通过属性(props) 和 状态(state)传递数据。
一、React 元素 与 DOM 元素 事件处理区别
(1)命名:React 事件使用驼峰命名,而不是全部小写
(2)写法上:
HTML里:
<!-- onclick里c小写,用引号包围内容(想想JSX怎么包裹变量你就懂了)-->
<!-- onclick传递的是函数的执行过程,
所以还可以像这样:onclick="alert('ss')"-->
<button onclick="activateLasers()">
Activate Lasers
</button>
React 中:
<!-- onClick里c大写,用{}包围内容-->
<!-- onClick里传递的必须是函数,如要加语句 必须像下面这样:
<button onClick=()=>{alert('ss')}>其实也是函数 -->
<button onClick={activateLasers}>
Activate Lasers
</button>
(3)阻止默认行为
在 React 中你不能通过返回 false(即 return false; ) 来阻止默认行为。必须明确调用 preventDefault 。
例如,阻止链接打开一个新页面的默认行为
HTML里:
<a href="#" onclick="
console.log('The link was clicked.');
return false;
">
Click me
</a>
而在 React 中, 应该这么写:
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}
return (
<a href="#" onClick={handleClick}>
Click me
</a>
);
}
ReactDOM.render(
<ActionLink/>,
document.getElementById('root')
);
或者这样写
function ActionLink() {
return (
<a href="#" onClick={() => {
e.preventDefault();
console.log('The link was clicked.');
}
}>
Click me
</a>
);
}
ReactDOM.render(
<ActionLink/>,
document.getElementById('root')
);
显然第一种写法更方便阅读。
这里需要注意的:我们是 给组件的元素 添加事件,而不是给组件添加事件
。
例如下面这种写法是错误
的
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}
return (
<a href="#">
Click me
</a>
);
}
ReactDOM.render(
//不是给组件添加事件,是给组件元素添加
<ActionLink onClick={handleClick}/>,
document.getElementById('root')
);
二、处理事件响应的方式
1、使用匿名函数
缺点:当事件响应逻辑比较复杂时,匿名函数的代码量会很大,会导致render函数变得臃肿,不容易直观地看出组件最终渲染出的元素结构。另外,每次render方法调用时,都会重新创建一个匿名函数对象,带来额外的性能开销,当组件的层级越低时,这种开销就越大,因为任何一个上层组件的变化都可能会触发这个组件的render方法。当然,在大多数情况下,这点性能损失是可以不必在意的。
例:
class MyComponent extends React.Component{
render(){
//ES6中默认this不与当前对象绑定,
//而箭头函数解决了this绑定这一问题,它可以将函数体内的this绑定到当前对象
return(
<button onClick={
() => {console.log('hhhh');}
}>
click
</button>
);
}
}
ReactDOM.render(
<MyComponent/>,
document.getElementById('root')
);
效果:(点击按钮后输出:hhhh)
2、使用组件方法
- 优点:每次render方法的调用,不会重新创建一个新的事件响应函数,没有额外的性能损失。
- 缺点:使用这种方式要在 构造函数 中 为事件响应的方法 进行手动绑定this: this.handleClick=this.handleClick.bind(this),这是因为ES6 语法的缘故,ES6 Class 的方法默认不会把this绑定到当前的实例对象上,需要我们手动绑定。
例1:
class MyComponent extends React.Component{
constructor(props){
super(props);
this.state = {number:0};
//手动绑定this
this.handleClick = this.handleClick.bind(this);
}
handleClick(){
this.setState({
number: ++this.state.number,
});
}
render(){
return(
<div>
<div>{this.state.number}</div>
<button onClick={this.handleClick}>click</button>
</div>
);
}
}
ReactDOM.render(
<MyComponent/>,
document.getElementById('root')
);
效果:同上
例2:
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// 这个绑定是必要的,使`this`在回调中起作用
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
效果:狠狠点击我
3、使用属性初始化语法
用到了ES7的特性,目前并非默认支持,需要Babel插件的支持,但是写法最为简洁,也不需要手动绑定this。但是这个特性还处于试验阶段。
class MyComponent extends React.Component{
constructor(props){
super(props);
this.state = {number:0};
}
handleClick = () => {
this.setState({
number: ++this.state.number,
});
}
render(){
return(
<div>
<div>{this.state.number}</div>
<button onClick={this.handleClick}>click</button>
</div>
);
}
}
ReactDOM.render(
<MyComponent/>,
document.getElementById('root')
);
这样一来,再也不用手动绑定this了。但是这个特性还处于试验阶段,默认是不支持的。如果你是使用官方脚手架Create React App 创建的应用,那么这个特性是默认支持的。你也可以自行在项目中引入babel的transform-class-properties插件获取这个特性支持。
三、处理事件响应 函数传参
事件响应函数默认是会被传入一个事件对象Event作为参数的。如果想传入其他参数给响应函数呢?
官方文档是这么说的:
但具体什么意思呢?看下面例子你就懂了
方法一:直接使用新参数
class MyComponent extends React.Component{
constructor(props){
super(props);
this.state = {
list:[1,2,3,4],
current:1
};
}
handleClick(item,event){
this.setState({
current: item,
});
alert(item);
}
render(){
return(
<ul>
{this.state.list.map(
(item) => (
<li className={this.state.current === item ? 'current':''}
onClick={(event) => this.handleClick(item,event)}>
{item}
</li>
)
)}
</ul>
);
}
}
ReactDOM.render(
<MyComponent/>,
document.getElementById('root')
);
输出:(实际应用场景中,上面代码应该是像下面一样遍历数组,但第一个项样式会不一样)。点击不同的项后,会alert出该项的值
方法二:
把绑定this的操作延迟到render中,在绑定this的同时,绑定额外的参数:
class MyComponent extends React.Component{
constructor(props){
super(props);
this.state = {
list:[1,2,3,4],
current:1
};
}
handleClick(item){
this.setState({
current: item,
});
alert(item);
}
render(){
return(
<ul>
{this.state.list.map(
(item) => (
<li className={this.state.current === item ? 'current':''}
onClick={this.handleClick.bind(this,item)}
>
{item}
</li>
)
)}
</ul>
);
}
}
ReactDOM.render(
<MyComponent/>,
document.getElementById('root')
);
效果:同上
方法三:
有没有感觉有鸡肋:虽然你不需要通过bind函数绑定this,但需要bind参数来绑定其他参数。
class MyComponent extends React.Component{
constructor(props){
super(props);
this.state = {
list:[1,2,3,4],
current:1
};
}
handleClick = (item) => {
this.setState({
current: item,
});
alert(item);
}
render(){
return(
<ul>
{this.state.list.map(
(item) => (
<li className={this.state.current === item ? 'current':''}
onClick={this.handleClick.bind(undefined,item)}
//或onClick={this.handleClick.bind(null,item)
>
{item}
</li>
)
)}
</ul>
);
}
}
ReactDOM.render(
<MyComponent/>,
document.getElementById('root')
);
不管你在响应函数中有没有显式的声明事件参数Event,React都会把事件Event作为参数传递给响应函数,且参数Event的位置总是在其他自定义参数的后面。
例如,在《方法二、方法三 》中, handleClick的参数中虽然没有声明Event参数,但你依然可以通过 arguments[1]获取到事件Event对象。