React技术栈之React(二)组件

前端之家收集整理的这篇文章主要介绍了React技术栈之React(二)组件前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

React组件

组件是React的基石,所有的React应用程序都是基于组件的。
React组件,可以通过React.createClass来声明:

import React from 'react';

var List = React.createClass({
    getInitialState: function() {
        return ['a','b','c']
    },render: function() {
        return (...);
    }
});

export default List;

上面的做法已不推荐,建议全部使用ES6写法,React官方也在第一时间就支持了ES6 class的写法。

ES6语法定义组件:

import React from 'react';

class List extends React.Component {
    constructor() {
        super();
        this.state = ['a','c'];
    }
    render() {
        return (...);
    }
}

export default List;

组件首字母必需大写。JSX语法使用第一个字母大小写来区分普通的HTML标签和React组件。

props属性

在组件内部通过this.props访问外部JSX传入的属性

组件Profile:

import React from 'react';

export default class Profile extends React.Component {
  // 渲染一个Vitrual DOM结构
  render() {
    return (
      <div className="profile-component">
        <h1>我的名字叫{this.props.name}</h1> 
        <h2>我今年{this.props.age}岁</h2>
      </div>
    )
  }
}

渲染Profile组件:

import ReactDOM from 'react-dom';
import Profile from './profile';

const app = document.createElement('div');
document.body.appendChild(app);
ReactDOM.render(<Profile name="zhutx" age=30 />,app);

或者使用...属性扩展

const props = {
  name: 'zhutx',age: 30
};

const app = document.createElement('div');
document.body.appendChild(app);
render(<Profile {...props} />,app);

React支持组件对传入属性的类型校验:

import { PropTypes } from 'react';

const propTypes = {
  // 验证不同类型的JavaScript变量
  optionalArray: PropTypes.array,optionalBool: PropTypes.bool,optionalFunc: PropTypes.func,optionalNumber: PropTypes.number,optionalObject: PropTypes.object,optionalString: PropTypes.string,// 可以是一个ReactElement类型
  optinalElement: PropTypes.element,// 可以是别的组件的实例
  optinalMessage: PropTypes.instanceOf(Message),// 可以规定为一组值其中的一个
  optinalEnum: PropTypes.oneOf(['News','Photos']),// 可以规定是一组类型中的一个
  optionalUnion: PropTypes.oneOfType([
    PropTypes.string,PropTypes.number,PropTypes.instanceOf(Message)
  ]),// 可以在最后加一个isrequired,表明这个属性是必需的,否则就会返回一个错误
  requiredFunc: React.PropTypes.func.isrequired
}

给Profile组件添加验证:

import React,{ PropTypes } from 'react';
// 需要验证属性
const propTypes = {
  name: PropTypes.string.isrequired,age: PropTypes.number.isrequired
};

class Profile extends React.Component {
  // render是这个组件渲染的Vitrual DOM结构
  render() {
    return (
      <div className="profile-component">
        <h1>我的名字叫{this.props.name}</h1>
        <h2>我今年{this.props.age}岁</h2>
      </div>
    )
  }
}

// 将验证赋值给这个组件的propTypes属性
Profile.propTypes = propTypes;

export default Profile;

state状态

state是组件内部属性。组件本身是一个状态机,它可以在constructor中通过this.state直接定义它的值,然后根据这些值来渲染不同的UI。当state值发生改变时,可以通过this.setState方法让组件再次调用render方法,来渲染新的UI。
改造一下上面的组件,添加一个点赞按钮,每单机一次,就给赞的次数加1:

// Profile.jsx
export default class Profile extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      liked: 0
    };
    // ES6 class的组件声明方式,不会把自定义函数绑定到实例上,需要手动绑定
    this.likedCallback = this.likedCallback.bind(this);
  }
}

likedCallback() {
  let liked = this.state.liked;
  liked++;
  this.setState({
    liked
  });
}

render() {
  return (
    <div>
      <h1>我的名字叫{this.props.name}</h1>
      <h2>我今年{this.props.age}岁</h2>
      <button onClick="this.likedCallback">给我点赞</button>
      <h2>总点赞数:{this.state.liked}</h2>
    </div>
  )
}

组件生命周期

1) 组件首次加载

  • getDefaultProps:只会在装载前调用一次,在组件中赋值的数据会被设置到this.props中。

  • getInitialState:只会在装载前调用一次,返回值被设置到this.state中。ES6的写法中已被constructor代替。

  • componentWillMount:可以在渲染之前做一些准备工作。

  • render:必要方法。返回一个ReactElement对象。render是一个纯函数,不应该有任何修改组件state或者和浏览器交互的情况。

  • componentDidMount:render之后调用,从这里开始获取组件的DOM结构。如果想让组件加载完毕后做一些额外操作(比如Ajax请求等),可以在这里进行。

2) 组件props更新

  • componentWillReceiveProps(object nextProps):在组件接收到新的props的时候触发,参数nextProps就是传入的新的props,可以用它来和this.props比较,来决定是否用this.setState实现UI重新渲染。

  • shouldComponentUpdate:重新render之前被调用,可以返回一个布尔值来决定组件是否要更新,默认返回true

  • componentWillUpdate:render之前被调用,可以在渲染之前做一些准备工作,和componentWillMount类似。

  • render:同首次加载的render

  • componentDidUpdate:render完成后调用,和componentDidMount类似。

3) 组件卸载

  • componentWillUnmount:在组件被卸载之前调用,可以在之类做一些清理工作

组合组件

一个组件可以包含多个其他组件,继续扩展一下上面的应用,显示一个爱好列表

// Hobby.jsx 爱好组件
import React,{ PropTypes } from 'react';

const propTypes = {
  hobby: PropTypes.string.isrequired
};

class Hobby extends React.Component {
  render() {
    return <li>{this.props.hobby}</li>
  }
}

Hobby.propTypes = propTypes;

export default Hobby;
// Profile.jsx 使用爱好的组合组件
import Hobby from './hobby';
...
constructor(props) {
  super(props);
  this.state = {
    liked: 0,hobbies: ['skateboarding','rock music']
  };
  ...
  render() {
    return (
      <div>
        <h1>我的名字叫{this.props.name}</h1>
        <h2>我今年{this.props.age}岁</h2>
        <button onClick="{this.likedCallback}">给我点赞</button>
        <h2>总点赞数:{this.state.liked}</h2>
        <h2>我的爱好:</h2>
        <ul>
          {this.state.hobbies.map((hobby,i) => <Hobby key={i} hobby={hobby} />)}
        </ul>
      </div>
    )
  }
}

只要将子组件看成自定义HTML标签就好了,传入想要的属性。特别注意要给每个循环组件添加一个唯一的key值。

无状态函数式组件

Hobby这类组件,没有内部state,不需要组件生命周期函数。可以用纯函数的形式来表达。它做的事情只是根据输入来生成组件,没有其他副作用

// 无状态函数
function Hobby(props) {
  return <li>{props.hobby}</li>
}

state设计原则

  创建尽量多的无状态组件,这些组件唯一关心的就是渲染数据。而在最外层,应该有一个包含state的父级别组件,用于处理各种事件、交流逻辑、修改state。对应的子组件要关心的只是传入的属性而已。
  state应该包含组件的事件回调函数可能引发UI更新的这类数据。在实际的项目中,应该是轻量化的JSON数据,尽量把数据的表现设计到最小,更多的数据可以在render中通过各种计算得到。
比如,有一个商品列表和用户已选购的商品列表,最直观的state设计如下:

{
  goods: [
    {
      "id": 1,"name": "paper"
    },{
      "id": 2,"name": "pencil"
    },...
  ],selectedGoods: [
    {
      "id": 1,"title": "hello world"
    }
  ]
}

这样做当然可以,但是根据最小化设计state原则,selectedGoods的商品就是goods里的几项。所以可以修改成:

selectedGoods: [1,2,3]

渲染时,只要把渲染的条目从goods中取出来就可以了。
state不应该包含哪些数据?为达到state最小化设计,下面这几种数据不应该包含在state中:

  1. 可以由state计算得到的数据。就像刚才的selectedGoods一样

  2. 组件。组件不需要保存到state中

  3. props中的数据。props可以看作是数据来源,它不需要保存在state中。

DOM操作

大多数情况下,不需要操作DOM去更新UI,应使用setState。但是有些情况确实需要访问一些DOM(如表达的值),那么可采用refs方式来获得DOM节点。只需要加个ref属性,然后通过this.refs.name来获得对应的DOM结构。

// Profile.jsx
render() {
  return (
    <div>
      ...
      <input type="text" ref="hobby" />
      <button onClick={this.addHobbyCallback}>添加爱好</button>
    </div>
  )
}

在button上添加事件,取得input的值,添加到state的值里面:

// Profile.jsx
addHobbyCallback() {
  // 用this.refs.name来取得DOM节点
  let hobbyInput = this.refs.hobby;
  let val = hobbyInput.value;
  if (val) {
    let hobbies = this.state.hobbies;
    // 添加值到数组
    hobbies = [...hobbies,val];
    // 更新state,刷新UI
    this.setState({
      hobbies
    },() => {
      hobbyInput.value = '';
    });
  }
}

猜你在找的React相关文章