組件

組件是React的基石,所有的React應(yīng)用程序都是基于組件的。
React組件,可以通過React.createClass來聲明:

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

建議全部使用ES6寫法,React官方也在第一時間就支持了ES6 class的寫法:

import React from 'react';

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

porps屬性

Profile.jsx組件
props就是傳入組件的屬性,由外部JSX傳入,在組件內(nèi)部通過this.props訪問

import React from 'react';

export default class Profile extends React.Component {
  // 渲染一個Vitrual DOM結(jié)構(gòu)
  render() {
    return (
      <div className="profile-component">
        <h1>我的名字叫{this.props.name}</h1> 
        <h2>我今年{this.props.age}歲</h2>
      </div>
    )
  }
}

把組件掛載到DOM節(jié)點上

import { render } from 'react-dom';
import Profile from './profile';

render(<Profile name="viking" age=20 />, document.getElementById('container'));

或者使用...屬性擴展

const props = {
  name: 'viking',
  age: 20
};
render(<Profile {...props} />, document.getElementById('container'));

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),

  // 可以規(guī)定為一組值其中的一個
  optinalEnum: PropTypes.oneOf(['News', 'Photos']),

  // 可以規(guī)定是一組類型中的一個
  optionalUnion: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.number,
    PropTypes.instanceOf(Message)
  ]),
  // 可以在最后加一個isRequired, 表明這個屬性是必需的,否則就會返回一個錯誤
  requiredFunc: React.PropTypes.func.isRequired
}

給剛才的組件添加驗證

import React, { PropTypes } from 'react';
// 需要驗證屬性
const propTypes = {
  name: PropTypes.string.isRequired,
  age: PropTypes.number.isRequired
};

class Profile extends React.Component {
  // render是這個組件渲染的Vitrual DOM結(jié)構(gòu)
  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狀態(tài)

state是組件內(nèi)部屬性。組件本身是一個狀態(tài)機,它可以在constructor中通過this.state直接定義它的值,然后根據(jù)這些值來渲染不同的UI。當(dāng)state值發(fā)生改變時,可以通過this.setState方法讓組件再次調(diào)用render方法,來渲染新的UI。
改造一下上面的組件,添加一個點贊按鈕,每單機一次,就給贊的次數(shù)加1:

// Profile.jsx
export default class Profile extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      liked: 0
    };
    // ES6 class的組件聲明方式,不會把自定義的函數(shù)綁定到實例上,需要手動綁定
    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>總點贊數(shù):{this.state.liked}</h2>
    </div>
  )
}

組件生命周期

組件加載

  • getDefaultProps:在組件中的賦值的數(shù)據(jù)會被設(shè)置到this.props中
  • getInitialState:返回值會被設(shè)置到this.state中
getInitialState: function() {
    return {
      count: 0
    };
}

ES6的寫法,只需寫在constructor中即可

constructor(props) {
    super(props);
    // 這里聲明state
    this.state = {count: 0};
}
  • componentWillMount:可以在渲染之前做一些準備工作。
  • render:必要方法。返回一個ReactElement對象。render是一個純函數(shù),不應(yīng)該有任何修改組件state或者和瀏覽器交互的情況。
  • componentDidMount:render之后調(diào)用,從這里開始獲取組件的DOM結(jié)構(gòu)。如果想讓組件加載完畢后做一些額外操作(比如Ajax請求等),可以在這里進行。

組件props更新

  • componentWillReceiveProps(object nextProps):在組件接收到新的props的時候觸發(fā),參數(shù)nextProps就是傳入的新的props,可以用它來和this.props比較,來決定是否用this.setState實現(xiàn)UI重新渲染。
  • shouldComponentUpdate:重新render之前被調(diào)用,可以返回一個布爾值來決定組件是否要更新,默認返回true
  • componentWillUpdate:render之前被調(diào)用,可以在渲染之前做一些準備工作,和componentWillMount類似。
  • render:同首次加載的render
  • componentDidUpdate:render完成后調(diào)用,和componentDidMount類似。

組件卸載

  • componentWillUnmount:在組件被卸載之前調(diào)用,可以在之類做一些清理工作

組合組件

一個組件可以包含多個其他組件,繼續(xù)擴展一下上面的應(yīng)用,顯示一個愛好列表

// 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>總點贊數(shù):{this.state.liked}</h2>
        <h2>我的愛好:</h2>
        <ul>
          {this.state.hobbies.map((hobby, i) => <Hobby key={i} hobby={hobby} />)}
        </ul>
      </div>
    )
  }
}

只要將子組件看成自定義HTML標(biāo)簽就好了,傳入想要的屬性。特別注意要給每個循環(huán)組件添加一個唯一的key值。


無狀態(tài)函數(shù)式組件

Hobby這類組件,沒有內(nèi)部state,不需要組件生命周期函數(shù)??梢杂眉兒瘮?shù)的形式來表達。它做的事情只是根據(jù)輸入來生成組件,沒有其他副作用

// 無狀態(tài)函數(shù)
function Hobby(props) {
  return <li>{props.hobby}</li>
}

state設(shè)計原則

創(chuàng)建盡量多的無狀態(tài)組件,這些組件唯一關(guān)心的就是渲染數(shù)據(jù)。而在最外層,應(yīng)該有一個包含state的父級別組件,用于處理各種事件、交流邏輯、修改state。對應(yīng)的子組件要關(guān)心的只是傳入的屬性而已。
  state應(yīng)該包含組件的事件回調(diào)函數(shù)可能引發(fā)UI更新的這類數(shù)據(jù)。在實際的項目中,應(yīng)該是輕量化的JSON數(shù)據(jù),盡量把數(shù)據(jù)的表現(xiàn)設(shè)計到最小,更多的數(shù)據(jù)可以在render中通過各種計算得到。
比如,有一個商品列表和用戶已選購的商品列表,最直觀的state設(shè)計如下:

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

這樣做當(dāng)然可以,但是根據(jù)最小化設(shè)計state原則,selectedGoods的商品就是goods里的幾項。所以可以修改成:

selectedGoods: [1, 2, 3]

渲染時,只要把渲染的條目從goods中取出來就可以了。
state不應(yīng)該包含哪些數(shù)據(jù)?為達到state最小化設(shè)計,下面這幾種數(shù)據(jù)不應(yīng)該包含在state中:

  1. 可以由state計算得到的數(shù)據(jù)。就像剛才的selectedGoods一樣
  2. 組件。組件不需要保存到state中
  3. props中的數(shù)據(jù)。props可以看作是數(shù)據(jù)來源,它不需要保存在state中。

DOM操作

大多數(shù)情況下,不需要操作DOM去更新UI,應(yīng)使用setState。但是有些情況確實需要訪問一些DOM(如表達的值),那么可采用refs方式來獲得DOM節(jié)點。只需要加個ref屬性,然后通過this.refs.name來獲得對應(yīng)的DOM結(jié)構(gòu)。

// 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節(jié)點
  let hobbyInput = this.refs.hobby;
  let val = hobbyInput.value;
  if (val) {
    let hobbies = this.state.hobbies;
    // 添加值到數(shù)組
    hobbies = [...hobbies, val];
    // 更新state, 刷新UI
    this.setState({
      hobbies
    }, () => {
      hobbyInput.value = '';
    });
  }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容