React 组件基本使用(一)

来源:互联网 发布:5到25位字符淘宝会员名 编辑:程序博客网 时间:2024/05/21 03:57

React 组件基本使用(一)

  React 提供了两种方式来声明组件,一种是函数式,一种是类式,就是用es6 class, 我们所有的组件都继承自React.Component.

  函数式很简单,就像我们平常写函数一个,接受一个参数作为输入,然后进行相应的输出,只不过它输出的jsx.

// welcome 函数式组件。function Welcome(props) {      return  <h1> {props.name}</h1>  }

  类式 写法如下:

class Welcome extends React.Component {  render() {    return  <h1> {this.props.name}</h1>  }}

  声明组件以后,怎么使用这个组件? 如果组件还要接受参数,怎样进行参数传递? 把组件想成一个html标签就可以了,html标签怎么使用,组件就怎么使用。html标签有两种使用方式,一种是 img 自闭合标签<img />,一种是 h1之类的双标签<h1></h1>。同理,组件也有这两种使用方式。传参则像是给html标签写属性,属性名 = 属性值,如name =”Jason” , 组件内部的props 则把这些属性组合在一起形成对象{name: “jason”}

<Welcome name="Jason" /> // 标签一定要闭合,就是后面的/不能忘记
<Welcome></Welcome>

  使用组件,它返回了 <h1> Jason</h1>, 很像html 代码,其实它是 React 所说的虚拟DOM,  并不是真实的DOM, 我们要把虚拟DOM 渲染成真实的DOM,才能显示到页面中,这需要用到ReactDOM的render 方法,它接受的第一个参数,就是虚拟DOM, 第二个参数就是我们要把DOM 渲染到什么地方。

ReactDOM.render(    <Welcome name="Jason" />,    document.getElementById('root'));

  这就是react组件最简单的使用,现在写一个html 页面,引入react reactdom 等,可以看到页面中输入了Jason

复制代码
<!DOCTYPE html><html>  <head>    <meta charset="UTF-8" />    <title>Hello World</title>    <script src="https://unpkg.com/react@latest/dist/react.js"></script>    <script src="https://unpkg.com/react-dom@latest/dist/react-dom.js"></script>    <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>  </head>  <body>    <div id="root"></div>    <script type="text/babel">            // welcome 函数式组件。      function Welcome(props) {          return  <h1> {props.name}</h1>        }      // 渲染成真实的DOM      ReactDOM.render(        <Welcome name="Jason" />,        document.getElementById('root')      );    </script>  </body></html>
复制代码

  属性值是一个字符串,是最简单的方式进行传值,其实属性值可以是任何的js 表达式,只要把它们包括在{}中,在jsx 中,{}里面的所有东西都当作js表达式进行解析。比如我们向组件中传递一个数字1,我们就可以写 num = {1}, 更改script 标签的内容进行测试

复制代码
      function Welcome(props) {         // 新增num 属性         return  <h1> {props.name} {props.num}</h1>        }      ReactDOM.render(        <Welcome name="Jason" num = {1}/>, //js表达式进行传值        document.getElementById('root')      );
复制代码

  如果我们要传递很多属性,这么 一个一个列出来,非常麻烦,这时可以使用对象,但如果用对象进行传值,又不符合 属性名=属性值的写法,这时要用到es6中的扩展运算符..., React 对es6 中的扩展运算符(…)进行扩展,它能运用到对象上,对对象进行分割。{…obj}; var obj = {name:”sam”, age: “28”}  {…obj}  => name=”sam” , age=”28”, 正好对应父组件向子组件传递数据。对上面的代码进行更改

复制代码
      var obj = {        name: "jason",        num : 28      }      function Welcome(props) {          return  <h1> {props.name} {props.num}</h1>        }      ReactDOM.render(        <Welcome {...obj}/>, //  对象传递数据        document.getElementById('root')      );
复制代码

  为了渲染时更加简单,我们可以把组件赋值给一个变量,然后在 render 方法中直接使用这个变量作为第一个参数, 这里对name 参数进行了更改,以便看出区别。可以看到页面中输出sam

复制代码
//  组件的调用结果赋值给一个变量const nameElem = <Welcome name="sam" /> ReactDOM.render(  nameElem,  //  在render 中直接使用变量  document.getElementById('root'));
复制代码

  有时我们还要把小的组件进行组合,形成大的组件,这里也可以用函数声明的方式

复制代码
//  组件的组合function App() {  return (    <div>      <Welcome name="Sam" />      <Welcome name="Jason" />    </div>  );}
复制代码

  对于这样的组件,在调用render 方法进行渲染的时候,要对函数名用html 标签进行包裹,形成一个自闭合标签。

ReactDOM.render(  <App />,  document.getElementById('root'));

  script 内容更改如下,可以看到页面上输出sam jason

复制代码
    <script type="text/babel">            // welcome 类式组件。      class Welcome extends React.Component {        render() {          return  <h1> {this.props.name}</h1>        }      }      //  组件的组合      function App() {        return (          <div>            <Welcome name="Sam" />            <Welcome name="Jason" />          </div>        );      }     ReactDOM.render(      <App />,  //自闭合标签      document.getElementById('root')    );    </script>
复制代码

   这时有两点需要注意:

    所有的组件名必须大写,以和普通的html 标签进行区分

    所有的组件都返回一个单一的根节点,这也是上面的render 方式中把所有元素都放到一个div 元素中的原因。

  使用class声明组件有一个好处,就是组件内部可以有自己的状态。给组件添加一个内部状态,用的是类的构造函数,因为构造函数保存的就是实例身上的属性,可以看成这个实例(组件)的状态, 我们组件也可以使用这个状态

复制代码
<script type="text/babel">            class Clock extends React.Component {        constructor(props){          super(props);          this.state = {date: new Date()}  // 给组件添加状态        }        render() {          return (            <div>              <h1>Hello World!</h1>              <h2>现在是{this.state.date.toLocaleTimeString()} </h2>              {/* 使用组件中状态*/}            </div>          )        }      }      // 渲染成真实的DOM      ReactDOM.render(        <Clock />,        document.getElementById('root')      );    </script>
复制代码

  在这里一定要注意调用构造函数时,super(),这是es6 的语法规定, 子类中没有this, 只能调用super生成子类的 this,如果在调用super 之前使用this, 就会报错。 这个地方其实是不会变化的,可以看成一个模式。每次给组件添加状态的时候,我们就按照这个模式书写就可以了。先写

 constructor(props) {    super(props);  }

  要添加什么状态,直接在构造函数里面super下面写this.state = …. 就可以了。

constructor(props){    super(props);    this.state = {date: new Date()}  // 给组件添加状态}

  这里还要涉及到jsx 中的注释,就是在render 方法中添加注释。要用{/* */}方式, {/* 要添加的注释*/}

  组件有状态, 这涉及到组件的生命周期,react 定义了非常完善的生命周期函数, 这时也简单地看一下。组件渲染到页面中叫挂载(mounting), 所以渲染完成后,叫做componentDidMount, 组件的卸载叫Unmount, 所以组件将要卸载 叫做componentWillUnmount。我们想要在什么时候使用状态,就可以直接调用生命周期函数,把想要做的事情写到函数里面,生命周期函数直接写在组件内部,比如,页面渲染完成后时间自动加一秒,这时还要涉及到组件的状态更改。React不允许直接更改状态, 或者说,我们不能给状态(如: date)进行赋值操作, 必须调用组件的setState() 方法去更改状态。这里写一个函数tick 来更改状态

tick() {  this.setState({    date: new Date()  })}

  面渲染完成后时间自动加一秒, 这里要调用componentDidMount, 

componentDidMount () {    this.timerId = setInterval(      () => this.tick()    , 1000)}

  整个完整的脚本如下,页面中就可以看到时间在走动

复制代码
class Clock extends React.Component {        constructor(props){          super(props);          this.state = {date: new Date()}  // 给组件添加状态        }                // 更改状态        tick() {          this.setState({            date: new Date()          })        }        // 生命周期函数        componentDidMount () {          this.timerId = setInterval(            () => this.tick()          , 1000)        }        render() {          return (            <div>              <h1>Hello World!</h1>              <h2>现在是{this.state.date.toLocaleTimeString()} </h2>            </div>          )        }      }      ReactDOM.render(        <Clock />,        document.getElementById('root')      );
复制代码

  tick 函数也可以直接写到组件里面,根据es6 class 语法的规定,直接写在类中的函数都会绑定在原型上,所以this.tick可以调用。但要保证this 指向的是我们这个组件,而不是其他的东西, 这也是在setInterval 中使用箭头函数的原因。

  React 中给元素添加事件,就像我们给元素添加行内事件一样简单,但这里也有不同的地方,事件名用驼峰命名法onClick, 事件处理函数是函数名,用{} 括起来, <button onClick={handleClick}>点击</button>。

  声明组件的方式有两种,对应的事件处理也有两种写法. 函数式声明比较简单,我们直接在函数内部声明一个函数作为事件处理函数,然后在render 方法渲染的时候直接给元素注册就好了。

复制代码
 // Button 组件    function Button() {      // 事件处理函数      function handleClick(e){        e.preventDefault();        console.log("clicked")      }      //注册事件      return  <button onClick={handleClick}>点击</button>    }
复制代码

  Es6 类式声明比麻烦,主要是因为this绑定问题。我们直接在组件内部写处理函数,和任何函数一样,但是如果函数中有this 的话,this却不指向这个组件。我们写一个组件,点击改变按钮上显示的值。

复制代码
 class Toggle extends React.Component {        constructor(props){          super(props);          this.state = {isToggleOn: true}  // 一个状态        }        handleClick() {          console.log(this)          this.setState(prevState => ({        // 改变状态            isToggleOn: !prevState.isToggleOn          }))        }        render() {          {/* 调用事件处理函数*/}          return (            <button onClick={this.handleClick}>                 {this.state.isToggleOn ? "On": "Off"}            </button>          )        }      }      ReactDOM.render(        <Toggle />,        document.getElementById('root')      );
复制代码

  在页面中点击可以看到handleClick中的事件处理函数console.log 输出是null, 然后报错了。这主要是由于this 是在函数运行的时候动态绑定的,this.handleClick指向了handleClick 函数,点击的时候,这个函数开始执行,但this 却没有指定,它是在哪个环境下执行, 由于 es6 class 是在严格模式下进行的,所以输出了null.

  很显然,我们必须使this指向我们这个组件,改变this指向可以使用两种方法,一种是ES5提供的bind()方法,它的第一个参数就是指定函数中this的,且它返回 一个函数,可以知道,返回的这个函数中this已经写死了,在程序运行的时候也不会变化了。在上面代码的构造函数中加上 this.handleClick = this.handleClick.bind(this)

constructor(props){   super(props);    this.state = {isToggleOn: true}     this.handleClick = this.handleClick.bind(this)}

  另一种是箭头函数,它里面的this继承于包围它的函数,那么我们直接把handleClick 事件处理函数写成箭头函数,里面的this 就指向我们这个组件。

handleClick = () => {     this.setState(prevState => ({            isToggleOn: !prevState.isToggleOn     }))}

  无论使用哪种方法,我们的程序都可以正常运行了。

原创粉丝点击