React 组件基本使用(一)

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/[email protected]/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
     }))
}

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

猜你喜欢

转载自blog.csdn.net/drivercode/article/details/78100811