Skip to content

React组件 #10

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
Wscats opened this issue Jun 16, 2017 · 0 comments
Open

React组件 #10

Wscats opened this issue Jun 16, 2017 · 0 comments

Comments

@Wscats
Copy link
Owner

Wscats commented Jun 16, 2017

React组件

React提供了两种方式来声明组件,一种是函数式,一种是类式,就是用ES6的class我们所有的组件都继承自React.Component
函数式很简单,就像我们平常写函数一个,接受一个参数作为输入,然后进行相应的输出,只不过它输出的jsx格式

函数式组件

注意组件只能有一个根元素

function Wscats(props) {  
    return  <h1> {props.name}</h1>  
}

//ES6
const Wscats = ({props}) => (
  <div>
    <h1>{props.name}</h1>
  </div>
)

类式组件

import React from 'react';
//推荐这种
class Wscats extends React.Component {
  render() {
    return  <h1> {this.props.name}</h1>
  }
}
//or 这种方法将要废弃
var Wscats = React.createClass({
  render() {
    return  <h1> {this.props.name}</h1>
  }
}

渲染组件

HTML标签怎么使用,组件就怎么使用

HTML标签有两种使用方式

  1. 一种是img自闭合标签<img />
  2. 一种是h1之类的双标签<h1></h1>

同理,组件也有这两种使用方式。传参则像是给html标签写属性,属性名 = 属性值,如name =”Wscats” , 组件内部的props,则把这些属性组合在一起形成对象{name: "Oaoafly"}

<Wscats name="Oaoafly" />//标签一定要闭合,就是后面的/不能漏掉
<Wscats></Wscats>

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

ReactDOM.render(
    {/*虚拟DOM*/}
    <Wscats name="Oaoafly" />,
    {/*DOM渲染的地方*/}
    document.getElementById('root')
);

render()应该是一个纯粹函数,即使不使用组件的state,每次调用都返回同样的结果,不读写DOM,不与浏览器交互,只做最单纯的渲染。当需要修改状态或有交互时,应该在componentDidMount等组件生命周期函数中进行

Props传值

属性值是一个字符串,是最简单的方式进行传值,其实属性值可以是任何的JS表达式,只要把它们包括在{}中,在jsx中,{}里面的所有东西都当作JS表达式进行解析,比如我们向组件中传递一个数字18,我们就可以写age = {18}

function Wscats(props) {
  // 新增num 属性
  return <h1>
    {props.name}
    {props.age}</h1>
}

ReactDOM.render(
  <Wscats name="Oaoafly" age={16}/>, //JS表达式进行传值
    document.getElementById('root'));

如果要传递多个属性, 一个一个传会很麻烦,这时可以使用对象,但如果用对象进行传值,又不符合属性名=属性值的写法,这时要用到ES6中的扩展运算符...,React对ES6中的扩展运算符(…)进行扩展,它能运用到对象上,对对象进行分割,{…obj}

var obj = {
    name: "Wscats",
    age: 16
  }
{…obj} => name = "Wscats", age = 16,

正好对应父组件向子组件传递数据

var obj = { name: "Wscats", age : 16 } //函数式组件
function Wscats(props) { 
return (
    <div>
      <h1>{props.name}</h1>
      <h1>{props.age}</h1>
    </div>
)} // 渲染成真实的DOM
ReactDOM.render(
    <Wscats {...obj} />, document.getElementById('root') );

组件Props传对象

类式组件和函数组件可以互相组合

//类式组件
class Wscats extends React.Component {
  render() {
    return <h1>
      {this.props.name}</h1>
  }
}
//函数组件的组合
function App() {
  return (
    <div>
      <Wscats name="Oaoafly"/>
      <Wscats name="Eno"/>
    </div>
  );
}
ReactDOM.render(
  <App/>, //自闭合标签
    document.getElementById('root'));

这时有两点需要注意:

  1. 所有的组件名必须大写,以和普通的html标签进行区分
  2. 所有的组件都返回一个单一的根节点,这也是上面的render方式中把所有元素都放到一个div元素中的原因

类式组件和函数组件组合

组件状态

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

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

//类式组件
class Wscats extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      date: new Date()
    } // 给组件添加状态
  }
  render() {
    return (
      <div>
        <h1>
          {this.props.name}</h1>
        <h2>现在时间是:{this.state.date.toLocaleTimeString()}</h2>
      </div>
    )
  }
}
//组件的组合
function App() {
  return (
    <div>
      <Wscats name="Oaoafly"/>
      <Wscats name="Eno"/>
    </div>
  );
}
ReactDOM.render(
  <App/>, //自闭合标签
    document.getElementById('root'));

所以往后在组件中添加状态,只要往this.state的对象中添加键值对就可以了

constructor(props){
    super(props);
    this.state = {name: "Hello Wscats"}  // 给组件添加状态
}

然后你就可以用表达式把state的状态呈现

<div>现在时间是:{this.state.date.toLocaleTimeString()}</div>

组件添加状态

注释

在render方法中添加注释,要用{/* */}方式

{/*要添加的注释*/}

生命周期

组件有状态,这涉及到组件的生命周期,react定义了非常完善的生命周期函数,组件渲染到页面中叫挂载(mounting),所以渲染完成后,叫做componentDidMount, 组件的卸载叫Unmount,所以组件将要卸载 叫做componentWillUnmount。我们想要在什么时候使用状态,就可以直接调用生命周期函数,把想要做的事情写到函数里面,生命周期函数直接写在组件内部

详情见 React组件生命周期

比如,页面渲染完成后时间自动加一秒,这时还要涉及到组件的状态更改。React不允许直接更改状态, 或者说,我们不能给状态(如: date)进行赋值操作, 必须调用组件的setState()方法去更改状态。这里写一个函数changeTime来更改状态

setState更改状态

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

//类式组件
class Wscats extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      date: new Date()
    } // 给组件添加状态
  }
  changeTime() {
    this.setState({date: new Date()})
  }
  // 生命周期函数
  componentDidMount() {
    setInterval(() => {
      this.changeTime()
    }, 1000)
  }
  render() {
    return (
      <div>
        <h1>
          {this.props.name}</h1>
        <h2>现在时间是:{this.state.date.toLocaleTimeString()}</h2>
      </div>
    )
  }
}
//组件的组合
function App() {
  return (
    <div>
      <Wscats name="Oaoafly"/>
      <Wscats name="Eno"/>
    </div>
  );
}

注意这里this的指向,如果不是箭头函数,this指向就是window

setInterval(function() {
  this.changeTime()
}.bind(this), 1000)
//or
setInterval(() => {
  this.changeTime()
}, 1000)

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

handleClick(e) {
    e.preventDefault();
    console.log(this);// null
    console.log("clicked");
}

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

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

this.handleClick = this.handleClick.bind(this)

解决方案1

也就是在构造函数中加上,此时调用this就可以查看修改到state的状态

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

解决方案2

这样也可以

<button onClick={this.handleClick.bind(this)}>点击</button>

解决方案3

<button onClick={() => this.handleClick()}>点击</button>

组件的this指向

生命周期相关参数,是React定义组件时提供的一系列处理函数(钓子函数),这些函数会在组件生命周期的某个阶段调用

创建期 getDefaultProps
object getDefaultProps()
创建期 getInitialState
object getInitialState()

在组件挂载前(即:创建期)调用一次,其返回值将做为this.state的初始值

getInitialState()方法会组件类创建的时候调用一次,其返回值会被缓存下来。该方法用于设置props属性的默认值,但仅对于非必须属性。如果父组件没有指定props中的某个值,此返回对象中的相应属性将会合并到this.props

getInitialState()方法会在组件实例创建前调用,这时还不能使用this.props属性,且其返回对象是在所有实例间共享的

创建期 componentWillMount
componentWillMount()

componentWillMount()服务器端和客户端都只调用一次,在初始化渲染执行之前被调用。如果在这个方法内调用setState()方法,render()方法将会收到更新后的state,也就是说这是我做在组件渲染前最后一个修改state的机会

创建期 componentDidMount
componentDidMount()

componentDidMount()会在组件初始化(渲染完成)后立即调用一次,我们一般在这个方法中使用this.getDOMNode()方法访问原始DOM

存在期 componentWillReceiveProps
componentWillReceiveProps(object nextProps)

componentWillReceiveProps()方法会在组件生命周期的存在期调用,当组件感知到props属性改变会,会调用此方法。render()方法将会在其后调用,这时我们可以通过this.setState()来阻止组件的再次渲染。

存在期 shouldComponentUpdate
boolean shouldComponentUpdate(object nextProps, object nextState)

shouldComponentUpdate()方法发生在组件生命周期的存在器,在组件收到新的props或state。在这个方法中,我们可以访问组件的props和state属性,通过这两个属性可以确认组件是否需要更新,如果不需要更新,则返回false,则其后的方法将不会在执行。如:

shouldComponentUpdate: function(nextProps, nextState) {
  return nextProps.id !== this.props.id;
}
存在期 componentWillUpdate
componentWillUpdate(object nextProps, object nextState)

componentWillUpdate()会在收到新的props或state后调用,类似componentWillMount()

存在期 componentDidUpdate
componentDidUpdate(object prevProps, object prevState)

componentDidUpdate()会在组件重新渲染后立即被调用,当我们需要在组件重新渲染后操作DOM则需要使用这个方法

销毁&清理期 componentWillUnmount
componentWillUnmount()

componentWillUnmount()是组件销毁&清理期唯一调用的方法,它会在组件从DOM中移除时被调用,这时我们可以清理一些状态或清理在componentDidMount中创建的DOM元素

样式

可以配置less样式,然后导入到组件,然后利用less的命名空间来约束组件的样式

import React, { Component } from 'react';
import {
	BrowserRouter as Router,
	Route,
	Link
} from 'react-router-dom';
import { matchRoutes, renderRoutes } from 'react-router-config';
import "./root.less";
//新版本的写法 推荐
//class Wscats extends React.Component {
class Wscats extends Component {
	render() {
		return( 
			<div id="root">
				<h1>Root</h1>
					<Link to="/">home</Link> <Link to="/index">index</Link>
					{/* child routes won't render without this */}
					{renderRoutes(this.props.route.routes)}
					{console.log(this)}
			</div>
		)
	}
}
module.exports = Wscats;

root.less

#root {
	h1 {
		font-size: 50px;
		color: green;
	}
}

导出多个组件

平时我们习惯一个单JSX文件导出一个组件,事实上,我们可以一个JSX文件导出多个组件,例如我们可以把一类相似功能的组件放在一起,然后类声明多个组件,然后再导出,我们就可以在使用的时候,导入一个组件,从而使用多个组件的功能

import React from "react";
// 第一个公有组件
class PublicA extends React.Component {
  constructor(props) {
    super(props)
    this.state = {}
  }
  render() {
    return (
      <div>
        第一个公有组件
      </div>
    )
  }
}

// 第二个公有组件
class PublicB extends React.Component {
  constructor(props) {
    super(props)
    this.state = {}
  }
  render() {
    return (
      <div>
        第二个公有组件
      </div>
    )
  }
}
// 默认导出一个,仅且一个
// export default PublicA
// 导出多个
export {PublicA, PublicB}

注意这里我们用的是export{xxx}而不是export default xxx

下面我们使用的时候就可以引入一个JSX文件,但是同时导入了PublicAPublicB组件了

import React from 'react';
// 导入多个组件
import {PublicA, PublicB} from "../components/public.jsx";
class PageB extends React.Component {
  constructor(props) {
    super(props);
    // 设置 initial state
    this.state = {};

  }
  componentDidMount() {}

  render() {
    return (
      <div>
        <PublicA/>
        <PublicB/>
      </div>
    )
  }
}
export default PageB;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant