React学习:事件处理

React :元素构成组件,组件又构成应用。
React核心思想是组件化,其中 组件 通过属性(props) 和 状态(state)传递数据。


一、React 元素 与 DOM 元素 事件处理区别

(1)命名:React 事件使用驼峰命名,而不是全部小写
(2)写法上:
HTML里:




React 中:



		

(3)阻止默认行为
在 React 中你不能通过返回 false(即 return false; ) 来阻止默认行为。必须明确调用 preventDefault 。
例如,阻止链接打开一个新页面的默认行为

HTML里:


  Click me

而在 React 中, 应该这么写:

function ActionLink() {
	  function handleClick(e) {
	    e.preventDefault();
	    console.log('The link was clicked.');
	  }
	
	  return (
	    
	      Click me
	    
	  );
}
ReactDOM.render(
  ,
  document.getElementById('root')
);

或者这样写

function ActionLink() {
      return (
         {
                e.preventDefault();
                console.log('The link was clicked.');
            }
        }>
          Click me
        
      );
}
ReactDOM.render(
  ,
  document.getElementById('root')
);

显然第一种写法更方便阅读。


这里需要注意的:我们是 给组件里的HTML元素 添加事件,而不是给组件本身添加事件
例如下面这种写法是错误

function ActionLink() {
      function handleClick(e) {
        e.preventDefault();
        console.log('The link was clicked.');
      }

      return (
        
          Click me
        
      );
}
ReactDOM.render(
  //不是给组件添加事件,是给组件元素添加
  ,
  document.getElementById('root')
);



二、绑定事件 及 this指向

1、匿名函数绑定事件,箭头函数解决this指向

缺点: 当事件响应逻辑比较复杂时,匿名函数的代码量会很大,会导致render函数变得臃肿,不容易直观地看出组件最终渲染出的元素结构。另外,每次render方法调用时,都会重新创建一个匿名函数对象,带来额外的性能开销,当组件的层级越低时,这种开销就越大,因为任何一个上层组件的变化都可能会触发这个组件的render方法。当然,在大多数情况下,这点性能损失是可以不必在意的。

例:

class MyComponent extends React.Component{
  render(){
	//ES6中默认this不与当前对象绑定,
	//而箭头函数解决了this绑定这一问题,它可以将函数体内的this绑定到当前对象
    return(
      
    );
  }
}
ReactDOM.render(
  ,
  document.getElementById('root')
);

效果:(点击按钮后输出:hhhh)
这里写图片描述

2、组件方法绑定事件,箭头函数解决this指向

(1)在绑定组件方法时,用箭头函数

class MyComponent extends React.Component{
    constructor(props){
      super(props);
      this.state = {number:0};
    }
    handleClick() {
      this.setState({
        number: ++this.state.number,
      });
    }
    render(){
      return(
        
{this.state.number}
); } } ReactDOM.render( , document.getElementById('root') );

(2)在定义组件方法时,用箭头函数
用到了ES7的特性,目前并非默认支持,需要Babel插件的支持,但是写法最为简洁,也不需要手动绑定this。但是这个特性还处于试验阶段。

缺点:
如果需要传参数(例如:《 三、处理事件响应 函数传参 方法一(2)》),这种方式会变得很鸡肋,多此一举:

 class MyComponent extends React.Component{
    constructor(props){
      super(props);
      this.state = {number:0};
    }
    handleClick = () => {
      this.setState({
        number: ++this.state.number,
      });
    }
    render(){
      return(
        
{this.state.number}
); } } ReactDOM.render( , document.getElementById('root') );

这样一来,再也不用手动绑定this了。但是这个特性还处于试验阶段,默认是不支持的。如果你是使用官方脚手架Create React App 创建的应用,那么这个特性是默认支持的。你也可以自行在项目中引入babel的transform-class-properties插件获取这个特性支持。


3、组件方法绑定事件,bind解决this指向
  • 优点:每次render方法的调用,不会重新创建一个新的事件响应函数,没有额外的性能损失。

(1)在绑定组件方法时,用bind解决this指向

class MyComponent extends React.Component{
    constructor(props){
      super(props);
      this.state = {number:0};
    }
    handleClick(){
      this.setState({
        number: ++this.state.number,
      });
    }
    render(){
      return(
        
{this.state.number}
); } } ReactDOM.render( , document.getElementById('root') );

(2)在constructor中,手动用bind解决this指向

  • 缺点:使用这种方式要在 构造函数 中 为事件响应的方法 进行手动绑定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(
        
{this.state.number}
); } } ReactDOM.render( , 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 (
      
    );
  }
}

ReactDOM.render(
  ,
  document.getElementById('root')
);

效果:狠狠点击我


三、处理事件响应 函数传参

事件响应函数默认是会被传入一个事件对象Event作为参数的。如果想传入其他参数给响应函数呢?


官方文档是这么说的:
React学习:事件处理_第1张图片

但具体什么意思呢?看下面例子你就懂了


方法一:用箭头函数(同时解决了this指向)

(1)

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,
      });
      console.log(item);
      console.log(event);
    }
    render(){
      return(
        
    {this.state.list.map( (item) => (
  • this.handleClick(item,event)}> {item}
  • ) )}
); } } ReactDOM.render( , document.getElementById('root') );

输出:(实际应用场景中,上面代码应该是像下面一样遍历数组,但第一个项样式会不一样)。点击不同的项后,会打印出该项的值
这里写图片描述
(2)

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,
      });
      console.log(item);
      console.log(event);
    }
    render(){
      return(
        
    {this.state.list.map( (item) => (
  • this.handleClick(item,event)}> {item}
  • ) )}
); } } ReactDOM.render( , document.getElementById('root') );

效果:同上


方法二:用bind()传参数(同时解决了this指向)

把绑定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(
        
    {this.state.list.map( (item) => (
  • {item}
  • ) )}
); } } ReactDOM.render( , 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(
        
    {this.state.list.map( (item) => (
  • {item}
  • ) )}
); } } ReactDOM.render( , document.getElementById('root') );

不管你在响应函数中有没有显式的声明事件参数Event,React都会把事件Event作为参数传递给响应函数,且参数Event的位置总是在其他自定义参数的后面。例如,在《方法二、方法三 》中, handleClick的参数中虽然没有声明Event参数,但你依然可以通过 arguments[1]获取到事件Event对象。


实战

例1

  • 考点: 状态提升、事件处理传参
  • 场景: 有App、Parent、Son三个组件,App是Parent的父组件,Parent是Son的父组件;要求在点击Son里的button时,将App的state.value值改为Son中index的值。
   class Son extends React.Component {
      render() {
        let index = 10;
        return (
          
        );
      }
    }

    class Parent extends React.Component {
      render() {
        return ();
      }
    }

    class App extends React.Component {
      constructor(props) {
        super(props);
        this.state = {value : 0};
      }
      handleClick(index,e) {
        console.log(e); 
        this.setState({value: index});
      }
      render() {
        console.log(this.state.value);
        return ( this.handleClick(index,e)} />);
      }
    }

    ReactDOM.render(
      ,
      document.getElementById('root')
    );

在这里插入图片描述
点击button前输出:0,
点击button后输出:
在这里插入图片描述

例2

前面都一样,要求在点击Son里的button时,将App的state.value值改为Parent中index值。

    class Son extends React.Component {
      render() {
        return (
          
        );
      }
    }

    class Parent extends React.Component {
      render() {
        let index = 10;
        return ( this.props.myHandleClick(index,e)} />);
      }
    }

    class App extends React.Component {
      constructor(props) {
        super(props);
        this.state = {value : 0};
      }
      handleClick(index,e) {
        console.log(e); 
        this.setState({value: index});
      }
      render() {
        console.log(this.state.value);
        return ( this.handleClick(index,e)} />);
      }
    }

    ReactDOM.render(
      ,
      document.getElementById('root')
    );

输出:同上

你可能感兴趣的:(web前端,React,React学习)