转载的~react

react入门教程二

2017-03-29  本文已影响73人  宸桐

react事件

由于react的虚拟DOM模式,react对所有的事件也都做了专门的处理和优化,官方的说法是引入了虚拟事件对象,将浏览器的事件进行了封装,有着浏览器本地事件西安通的属性和方法,但是没有兼容问题。听起来不错,回到我们之前state的demo,handleClick(e){}加一个console.log(e)来打印一下这个点击事件:

5

这里可以看到我们打印出来的全部是空,这是因为react给我们做了处理,把不需要的值全部隐藏了。现在我们来改一下console.log(e.target) :

6

可以看到target已经被打印出来了。如果你有时候需要的是本地的event的话可以使用e.nativeEvent来调用:

7

这里就是浏览器原生的event事件,但是需要注重浏览器兼容性。所以一般情况下最好使用react封装好的event事件,react支持的完整事件列表可以在官方文档查看,这里不再一一列举。

tip:如果需要阻止默认的事件,比如form表单的默认提交,a标签的href... 必须使用e.preventDefault() 。而需要阻止事件冒泡则需使用e.stopPropagation()。

React事件机制不同于我们以往的js事件,为提高效率,它对事件做了二次处理,将所有项目中注册的事件全部代理(delegate)到顶层的document上,当你点击某个DOM的时候,它会找到这个DOM和其Component,并冒泡到找到对应的事件并触发。

比如我们经典的 ul li 结构,传统js使用事件委托将事件绑定在ul上,而现在使用react只需要在ul上绑一个click事件,每次点击ul里面的元素都会冒泡并触发到ul的事件上去。但是要注意如果你在ul里面再绑定一个事件,同时不使用stopPropagation去阻止这个冒泡事件,那他会一直执行到最顶层的事件上去。

想要深入了解React事件机制?相信这篇博客会对你有所帮助——React源码解读系列 -- 事件机制

Refs & 真实DOM

由于react使用虚拟DOM的结构,所有的页面变动都先在虚拟DOM上处理,只有当他们真正插入到文档中才会转化成真实DOM,react正是通过这样的机制来减少DOM操作从而提高页面的性能。

但是有时候我们确实有获取DOM的需求,前面在学习事件的时候我们使用e.target可以获取到真实的DOM。

而同时react也给我们提供了refs 来处理类似的情况。ref在react中是一个特殊的属性,它可以是一个回调函数也可以是一个字符串。如果你查找国内的资料你会发现几乎所有人用的都是ref的字符串属性,但是在最新版(V15.4.2)的react文档中,ref的字符串属性被称为’Legacy API‘ ,官方强调字符串的ref存在一些问题,并且很可能在未来的版本中将其移除。

我们先来看一下官方的demo:

var MyComponent = React.createClass({
  handleClick: function() {
    this.textInput.focus();
    console.log(this);
    console.log(this.textInput)
  },
  render: function() {
    return (
      <div>
        <input type="text" ref={(input) => { this.textInput = input; }} />
        <input type="button" value="Focus the text input" onClick={this.handleClick} />
      </div>
    );
  }
});

ReactDOM.render(
  <MyComponent />,
  document.getElementById('example')
);

ref的回调函数会在组件创建或者重新渲染时立即执行,回调函数的参数即为当前组件的真实DOM,我们可以立即使用这个DOM或者将其保存以备后用。

上面代码中输入框的ref使用es6语法直接在组件中创建了一个回调函数,这个函数将参数input保存到this.textInput中,我这里把 this 和 this.textInput 打印出来来帮助大家更好去理解:

8

这里this指向ReactElement,即当前的的react组件,通过回调函数往this里添加了textInput对象。通过下面的this.textInput就可以看到textInput为该组件的原生DOM,我们通过ref的回调函数将该DOM存在this中,方便后面随时调用。

简单的ref回调我们可以直接写在标签中,如果复杂的话我们可以将其抽出为一个函数,在标签中通过 ref={this.funtionName} 的形式调用。

同时我这里也有一个string refs的demo,虽然现在有很多人这样用,但是我们要尽量避免这样的写法:

var MyComponent = React.createClass({
  handleClick: function() {
    this.refs.myTextInput.focus();
  },
  render: function() {
    return (
      <div>
        <input type="text" ref="myTextInput" />
        <input type="button" value="Focus the text input" onClick={this.handleClick} />
      </div>
    );
  }
});

ReactDOM.render(
  <MyComponent />,
  document.getElementById('example')
);

这里给ref定义一个字符串,通过refs获取到页面所有的ref,再通过ref的字符串定位到该DOM,这样操作确实方便很多,所以这个方法被广为流传,但是建议有使用这样方法的童鞋最好慢慢抛弃这种写法。

tips:有些童鞋可能网上查找资料会看到getDOMNode() or findDOMNode() 方法,在早期react中确实存在这些方法结合ref来获取DOM,但是后来React拆分出ReactDOM后这些方法被归到ReactDOM 的方法中,直接在react中是不能使用的。

react表单

在开始之前我们先看一个例子:

var Form = React.createClass({
  render: function() {
    return (
      <input type='text' value='这是一个输入框'/>
    )
  }
})
ReactDOM.render(
  <Form/>,
  document.getElementById('example')
);

乍一看是不是和我们平时HTML的写法基本一样,但是一运行你就会发现无论如何你改变不了input中的值,键盘的输入对它没有任何影响!

react官方将带有value的表单定义为受限组件 ,即该组件受限制于value值,他将永远等于value的值。当然,不代表我们就不能定义表单的默认值了,官方给出了两种解决办法:

1,使用defaultValue。既然有受限组件,那就存在不受限组件。官方定义为不设置value的组件就是一个不受限组件,而不受限组件可以实时响应用户的输入将其反映到元素上。所以react就给组件定义了defaultValue来给组件设置一个非空的初始值,而对于radio,checkbox则有对应的defaultCheckd属性来代替checkd:

 <input type='text' defaultValue='这里是默认值,也可以使用state'/>

我们可以给input定义ref的回调来获取输入的值:

 <input type='text' ref={(input) => { this.textInput = input; }} defaultValue='这里是默认值,也可以使用state'/>

然后通过事件获取this.textInput.value。

比如我们一个页面有很多输入框,可以给他们分别定义ref,然后通过submit按钮的提交事件来一次性全部获取所有的输入值。

tip:如果我们需要阻止默认的表单提交事件,需要使用e.preventDefault(),这一点我们在之前的事件里提到过。 ( ) => { } 为ES6语法,它表示为 function( ) { }

2,对于使用state来给组件设置初始值的,可以给组件绑定onChange事件,通过onChange来实时获取用户输入,动态的改变state的值。这样虽然组件受限于value,但是value的值发生了变化,那么组件的值也就跟着变化:

var Form = React.createClass({
  getInitialState:function(){
    return {
        inputValue: 'input',
    }
  },
  handleInput:function(e) {
    this.setState({inputValue: e.target.value});
  },
  render: function() {
    return (
      <input type='text' value={this.state.inputValue} onChange={this.handleInput}/>
    )
  }
})
ReactDOM.render(
  <Form/>,
  document.getElementById('example')
);

以上是使用e.target.value来获取输入值来刷新state,当然你也可以使用ref来获取。

如果页面中有很多表单,但是我们又不想使用上面的defaultValue+ref,或者我们需要在用户输入的时候就获取输入值来检查。岂不是每一个表单组件都要定义一个onChange事件?

官方也考虑到这种问题,所以给我们提供了一个更好的解决方案:

var MyComponent = React.createClass({
  getInitialState: function() {
    return {
      isGoing: true,
      numberOfGuests: 2
    };
    this.handleInputChange = this.handleInputChange.bind(this);
  },

  handleInputChange(event) {
    const target = event.target;
    const value = target.type === 'checkbox' ? target.checked : target.value;
    const name = target.name;
    this.setState({
      [name]: value
    });
  },
  
  render: function() {
    return (
      <form>
        <label>
          Is going:
          <input
            name="isGoing"
            type="checkbox"
            checked={this.state.isGoing}
            onChange={this.handleInputChange} />
        </label>
        <br />
        <label>
          Number of guests:
          <input
            name="numberOfGuests"
            type="number"
            value={this.state.numberOfGuests}
            onChange={this.handleInputChange} />
        </label>
      </form>
    );
  }
});

ReactDOM.render(
  <MyComponent />,
  document.getElementById('example')
);

通过一个onChange事件来解决所有的表单,是不是很酷!

tip: [name] 为ES6语法,它表示在对象中,name为一个变量。

tip:<textarea> 常规设置默认值采用 <textarea>这里是默认值</textarea><select> 常规设置默认值是在 <option> 中添加selected,而这对于React来说会显得很奇怪,且不方便在后续的使用中来更新默认值。所以React中所有表单的默认值设置全部使用value(defaultValue)来定义,详细说明参见官方文档

生命周期

组件的生命周期分为三种:

react为每个状态提供两种处理函数,will在函数状态之前调用,did则在函数状态之后调用:

此外还有三种特殊状态的处理函数:

上一篇下一篇

猜你喜欢

热点阅读