1. 程式人生 > >react學習總結2--基礎(二)

react學習總結2--基礎(二)

react 學習總結–基礎(二)

說明

React 版本 :”15.4.1”

react-tap-event-plugin 版本: “^2.0.1”

1.資料流

在 React 中,資料流的流向是單向的–從父節點傳遞到子節點,因而元件是簡單且易於把握的,他們只需從父節點獲取props渲染即可

React 元件內部還具有自己的狀態,這些狀態只能在元件內修改,React元件本身很簡單,可以把它看成是一個函式,他接受 props 和 state 作為引數,返回一個虛擬DOM表現

Props(屬性)

props 就是 properties 的縮寫,你可以使用它把任意型別的資料傳遞給元件。可以在掛在元件的時候設定它的props

    let obj = {name : 'aaa', age : 20 };
    <Child obj={obj} />

通過元件例項的setProps方法(很少這樣做)來設定props

    let obj = {name : 'aaa', age : 20};
    let myChild = ReactDOM.render(
      <Child />,
      document.getElementById('app')
    );
    myChild.setProps({name : 'bbb'});

注:在元件內,props只能由父元件傳遞進來,子元件內不能修改自己的 props ,即不能通過this.setProps,或者this.props直接修改props

props的格式,可以設定為字串,<a href="/home"></a>
也可以使用{},注入JavaScript傳遞任意型別的變數 <a href={'/home/' + this.state.id}></a> ,新增事件處理 <a onClick={this.handleClick}></a> 也可以使用展開語法 <Child {...props} />

State(狀態)

React 將元件看成一個狀態機,有一個初始狀態,然後隨著與使用者的互動,導致狀態變化,從而觸發重新渲染UI 每一個React元件都可以擁有自己的state,元件的state只存在於元件內部

state可以通過setState來修改,也可以通過getInitialState方法提供一組預設值,只要setState被呼叫,render方法就會被呼叫,如果render返回值有變化,虛擬DOM就會更新

注:不能使用this.state.name = 'name'這樣的方式修改state,只能通過this.setState({name : 'name'})這種方式修改

    class Test extends Component {
    constructor(props) {
        super(props);
        this.state = {
          num : 0
        };
    }
    componentDidMount() {
      this.setState({num : 2});
    }
    render() {
        return (
            <Child childNum={this.state.num} />
        );
    }
}
PropTypes(props驗證)

元件的屬性可以是任意值,但有時候我們需要驗證別人使用元件的時候,提供的引數是否符合要求,PropsTypes屬性,就是React提供的驗證傳入props的有效性的方式
當向props傳入無效資料時,JavaScript控制檯會丟擲警告

為了效能考慮,只在開發環境驗證propsTypes

    class Modal extends Component {
        static propTypes = {
            children: PropTypes.node, //表示這些 props 是可傳可不傳的
            isOpen: PropTypes.bool.isRequired, //表示這些 props 不可以為空
        }
        ...
    }

這裡有更詳細的驗證器

2.事件處理

React 事件處理本質上跟JavaScript事件一樣,命名上與JavaScript一致,並且會在相同的情景下觸發,React標準化了事件物件,使用駝峰式命名 <input type="button" onClick={this.handleClick.bind(this)}>,顯式的呼叫bind(this),將函式上下文繫結到元件例項上。

事件捕獲處理

一般寫法onClick={this.handleClick.bind(this)}會在事件冒泡階段觸發,如果想在事件捕獲階段觸發需要加上Capture,例如 onClickCapture={this.handleCaptureClick.bind(this)}

虛擬事件物件

React 的事件物件event是經過封裝的,沒有瀏覽器相容問題,他有和瀏覽器本地事件相同的屬性和方法
但是如果需要呼叫底層的瀏覽器事件物件,只需要使用nativeEvent屬性就可以獲取,
常用的屬性和方法如下

  • stopPropagation() 阻止事件冒泡
  • preventDefault() 阻止預設事件
  • target 觸發事件的物件
  • currentTarget 繫結事件的物件
  • nativeEvent 原生瀏覽器事件物件
  • type 事件型別
觸控事件

舊版本的 React 為了使觸控事件生效,需要在渲染所有元件之前呼叫React.initializeTouchEvents(true)
但是在新版本的 React 中已經停用,要使用觸控事件需要引入react-tap-event-plugin,然後在渲染所有元件之前,呼叫injectTapEventPlugin();

為避免300ms之後再次觸發onClick(也就是點選一次,反應兩次)可以進行配置

    injectTapEventPlugin({
      shouldRejectClick: function (lastTouchEventTimestamp, clickEventTimestamp) {
        return true;
      }
    });

3.複合元件

本質上,一個元件就是一個JavaScript函式,它接受props和state兩個引數,並輸出渲染好的HTML,元件一般被用於呈現和表達應用的某部分資料

我們可以通過建立多個元件來合成一個元件,即把元件的不同功能點進行分離

    class ChildBtn extends Component{
      constructor(props) {
          super(props);
      }
      render() {
          return <button>點選</button>;
      }
    }
    class ChildInput extends Component{
      constructor(props) {
          super(props);
      }
      render() {
          return <input type="text" />;
      }
    }
    class Test extends Component {
        constructor(props) {
            super(props);
        }
        render() {
            return (
               <div>
                 <ChildInput />
                 <ChildBtn />
               </div>
            );
        }
    }
子級

<Parent><Child /></Parent>

如上,Child 就是 Parent 的子級,在Parent中能通過this.props.children讀取子級

4.Mixin

mixin允許我們定義可以再多個元件中共用的方法,它們就是混合近元件中的物件而已,React的Mixin
能夠防止靜默函式覆蓋,同時支援多個Mixin混合

React.createClass({
    mixins : [{
        getInitialState: function(){return {a : 1}}
    }],
    getInitialState: function(){return {b : 2}}
});
//最終結果:state => {a:1,b:2}
ES6 寫法

如果要使用ES6編寫React元件,不建議使用Mixins機制,將會使用高階元件代替代Mixins,更多詳情請看
高階元件替代Mixins

5.DOM操作

多數情況下,React的虛擬DOM足以用來建立你想要的使用者體驗,但是有些情況下不得不操作底層的DOM,例如:使用第三方類庫,或者執行一個React沒有原生支援的操作,為了是這些操作變得容易,React提供了一個可以用於處理受其自身控制的DOM節點的方法,這些方法在元件生命週期的特定階段才能被訪問到

ref屬性

ref屬性,用來配合獲取真實的DOM節點,下邊是一個例子

    class Test extends Component {
        constructor(props) {
            super(props);
            this.state = {
              value : ''
            }
        }
        handleClick(){
            let textInput = ReactDOM.findDOMNode(this.refs.text);
            console.log(textInput); // 獲取到真實DOM元素
        }
        handleChange(e){
          this.setState({value : e.target.value});
        }
        render() {
            return (
               <div>
                <input type="text" 
                  ref="text"
                  value={this.state.value}
                  onChange={this.handleChange.bind(this)}
                />
                <button onClick={this.handleClick.bind(this)}>獲取輸入內容</button>
               </div>
            );
        }
    }

訪問到相應的DOM節點,需要通過this.refs.[refName]找到相應的子元件,定義ref,必須保障賦給每個子元件的ref值在所有的子元件中是唯一的,找到子元件後,通過ReactDOM.findDOMNode()方法訪問到底層的DOM節點

注意:只有在元件被掛載後才能訪問,在有些生命週期中可以訪問到,但獲取的DOM不是最新的,以下是其執行環境

  • componentDidMount
  • componentDidUpdate
  • 事件處理函式中(事件處理函式都是在元件掛載後觸發)

注:舊版本會用this.refs.text.getDOMNode()的方式獲取,新版本中已經不能使用,替換為ReactDOM.findDOMNode(this.refs.text)

注:有其他方式能夠實現功能時,儘量不要使用此功能,在效能上會有一定障礙,同時增加了元件的複雜性

6.表單

表單元件不同於其他元件,因為他們可以通過使用者交互發生變化
表單元件支援幾個受使用者互動影響的屬性

  • value,用於 <input><textarea> 元件。
  • checked,用於型別為 checkbox 或者 radio<input> 元件。
  • selected,用於 <option> 元件。
無約束元件

即表單元件的值是不受React控制的,在React中,這種行為與設定<input /> 的 defaultValue一致
我們可以通過設定defaultValue屬性設定預設值

    class MyForm extends Component{
      render() {
        return (
          <input type="text" defaultValue="hello" /> 
        );
      }
    }

這個例子展示的就是無約束元件,元件的value並非由父元件設定,而是讓<input />自己控制自己的值

約束元件

也就是表單元件的狀態交由React元件控制,狀態值被儲存到React元件的state中

    class MyForm extends Component{
      constructor(props){
        super(props);
        this.state = {
          value
        };
      }
      handleChange(e){
        this.setState({value : e.target.value})
      }
      render() {
        return (
          <input type="text" 
            value={this.state.value} 
            onChange={this.handleChange.bind(this)} 
          /> 
        );
      }
    }

<input />的值儲存在父元件的state中

文字框和select

React 對<textarea /><select/>做了一些修改,提升了一致性

    <textarea name="description" value="This is a description." />
    <select value={this.state.value} onChange={this.handleChange.bind(this)}>
        <option value="A">fir</option>
        <option value="B">sec</option>
        <option value="C">thr</option>
    </select>
單選框和複選框
    class Test extends Component{
      constructor(props){
        super(props);
        this.state = {
          checked : false
        };
      }
      handleChange(e){
        this.setState({checked : e.target.checked})
      }
      render() {
        return (
          <input type="checkbox" 
            checked={this.state.checked} 
            onChange={this.handleChange.bind(this)} 
          /> 
        );
      }
    }

7.會用到的API

ReactDOM.unmountComponentAtNode

ReactDOM.unmountComponentAtNode(DOMElement containe)銷燬指定容器內的所有React節點。

  ReactDOM.unmountComponentAtNode(document.getElementById('app'));

注:只能銷燬ReactDOM.render(<app />,document.getElementById('app')) 中的容器內部節點,通過react產生的節點使用此方法回報錯:

  ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(this.refs.remove));
  warning.js:36 Warning: unmountComponentAtNode(): The node you're attempting to unmount was rendered by React and is not a top-level container. Instead, have the parent component update its state and rerender in order to remove this component.
React.isValidElement

React.isValidElement(object)判斷一個物件是否是一個ReactElement(不是很常用)

  let c = <Test />;
  console.log(React.isValidElement(c));//true

只有在判斷的物件是例項化的react元件時,才會返回true

React.cloneElement()

React.cloneElement()克隆並返回一個新的 ReactElement (內部子元素也會跟著克隆),新返回的元素會保留有舊元素的 props、ref、key。可以傳入三個引數
1.要克隆的ReactElement;2.需要新新增的屬性props;3.重新設定的子節點(會替換掉原本的子節點)

  render() {
    let span = <span ref="span">aaa</span>;
    let spanChange = React.cloneElement(span, {name:'aaa'} ,<em>bbb</em>);
    return (
      <div>
        {spanChange}
      </div>
    );
  }             
  //結果:<span name="aaa"><em>bbb</em><span>
React 遍歷

React.Children.map()可以實現遍歷,但是我一般直接用map

  render() {
    let arr = ['A','B','C','D'];
    return (
      <div>
        <ul>
          {
            arr.map((item,i) => {
              return (<li key={i}>{item}</li>)
            })
          }
        </ul>
      </div>
    );
  }

注意:像這樣迴圈新增react節點的操作一定要加上key屬性,否則會有警告:

warning.js:36Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method ofTest. See https://fb.me/react-warning-keys for more information.

forceUpdate

forceUpdate() 就是重新執行render,有些變數不在state上,但是又想達到變數更新,重新render的效果的時候,就可以使用此方法手動觸發render

  handleClick(){
    this.forceUpdate(function () {
      console.log('update'); //render之後的回撥
    });
  }
一些使用es6就不能使用的API
  • replaceState
  • isMounted
  • Mixin

下一篇–React-Router傳送門

  • API,及常用方法