React入門系列 - 3 state與props

yodfz發表於2019-01-10

3.1 什麼是state

我們要認識到,React中的元件其實是一個函式,所以state是函式內部的私有變數,外部其他元件或者方法都是無法直接訪問到內部的state。 而state主要被設計用於維持元件內部私有狀態。

3.1.1 初始化state

初始化state需要在class中constructor進行。

import React, { PureComponent } from 'react'

export default class index extends PureComponent {
  constructor(props){
    super(props)
    this.state = {name:'demo react',time:new Date()}
  }
  render() {
    return (
      <div>
        Hello world React!{this.state.name}
        <p>元件生成時間:{this.state.time}</p>
      </div>
    )
  }
}

複製程式碼

在這個程式碼中,我們初始化了name,time兩個state值。一旦state初始化完成,我們就可以在render中進行訪問。

使用npm run dev命令執行,並在瀏覽器中開啟檢視吧。

3.1.2 修改state值setState

我們已經知道了如何初始化元件中的state值,那麼接下來我們來看看,如何實現修改state的值

  import React, { PureComponent } from 'react'

  export default class index extends PureComponent {
    constructor(props){
      super(props)
      this.state = {name:'demo react',time:+new Date()}
    }
    handleUpdateName () {
      this.state.time = (+new Date())
    }
    render() {
      return (
        <div>
          Hello world React!{this.state.name}
          <p>元件生成時間:{this.state.time}</p>
          <button onClick={this.handleUpdateName.bind(this)}>修改值</button>
        </div>
      )
    }
  }
複製程式碼

有些動作快的同學,第一個想法就是如此修改元件中的state值,但是值得玩味的是,值的確是修改成功了,但是,並沒有實時體現在介面上。這究竟是怎麼回事呢?

這就要來看看,React中的setState方法。

React對於setState的定義為請求React修改某個資料,而React的實現則是將對變數的修改放入一個修改佇列中,在一個迴圈之後進行批量更新結果(深入點涉及VDom的更新機制)。所以,這裡會造成一個問題,就是setState資料之後立刻進行讀取,可能你讀取到的資料,並非是已經被更新過的有效值。

setState有三種修改資料的方式,讓我們來一個一個嘗試。

3.1.2.1 直接賦值
  import React, { PureComponent } from 'react'

  export default class index extends PureComponent {
    constructor(props){
      super(props)
      this.state = {name:'demo react',time:+new Date()}
    }
    handleUpdateName () {
      // this.state.time = (+new Date())
      console.log('修改前的值',this.state.time)
      this.setState({time:+new Date()})
      console.log('修改後的值',this.state.time)
      let that = this
      setTimeout(function(){
          console.log('當前state值',that.state.time)
      })
    }
    render() {
      return (
        <div>
          Hello world React!{this.state.name}
          <p>元件生成時間:{this.state.time}</p>
          <button onClick={this.handleUpdateName.bind(this)}>修改值</button>
        </div>
      )
    }
  }

複製程式碼

點選按鈕,控制檯輸出了不同的值,可以觀察到setState是採用非同步佇列模式的。

Alt text

3.1.2.2 賦值完成等待同步完成之後執行回撥

現在出現了一個問題,如果我們需要通過等待setState修改完成的值之後,應該如何處理?React為我們的setState提供了第二個引數callback

  import React, { PureComponent } from 'react'

  export default class index extends PureComponent {
    constructor(props){
      super(props)
      this.state = {name:'demo react',time:+new Date()}
    }
    handleUpdateName () {
      // this.state.time = (+new Date())
      console.log('修改前的值',this.state.time)
      this.setState({time:+new Date()},(function(){
          console.log('當前state值',that.state.time)
      })
      console.log('修改後的值',this.state.time)
    }
    render() {
      return (
        <div>
          Hello world React!{this.state.name}
          <p>元件生成時間:{this.state.time}</p>
          <button onClick={this.handleUpdateName.bind(this)}>修改值</button>
        </div>
      )
    }
  }

複製程式碼

再次執行,並且點選按鈕。我們可以看到控制檯輸出的值是跟第一個方法是一致的。

Alt text

現在我們來進行一次腦暴,可不可以直接修改state值呢?因為我們在最早直接對state修改的時候,React並未關閉這個物件的set方法。那麼我們可否直接通過修改state來進行渲染呢?React中的一個方法為我們解決了這個疑問。

  import React, { PureComponent } from 'react'

  export default class index extends PureComponent {
    constructor(props){
      super(props)
      this.state = {name:'demo react',time:+new Date()}
    }
    handleUpdateName () {
        console.log("修改前的值", this.state.time);
        this.state.time = (+new Date())
        console.log("修改後的值", this.state.time);
        console.log("當前state值", this.state.time);
        this.forceUpdate()
    }
    render() {
      return (
        <div>
          Hello world React!{this.state.name}
          <p>元件生成時間:{this.state.time}</p>
          <button onClick={this.handleUpdateName.bind(this)}>修改值</button>
        </div>
      )
    }
  }
複製程式碼

上面這個程式碼僅僅用於腦暴,參考,不要在生產環境中使用,因為這個會造成React渲染演算法與各種Hook失效,造成全域性重新渲染。

3.1.2.3 通過原始資料進行更新

在某些場景下面,我們可能是新的值是基於上一次的值推算而來,所以React提供了setState傳遞進方法來進行推算處理。

import React, { PureComponent } from "react";

export default class index extends PureComponent {
  constructor(props) {
    super(props);
    this.state = { name: "demo react", time: +new Date() };
  }
  handleUpdateName() {
    console.log("修改前的值", this.state.time);
    let that = this;
    this.setState(oldData => {
      return { time: oldData.time + 1000 };
    });
    console.log("修改後的值", this.state.time);
    setTimeout(function() {
      console.log("當前state值", that.state.time);
    });
  }
  render() {
    return (
      <div>
        Hello world React!{this.state.name}
        <p>元件生成時間:{this.state.time}</p>
        <button onClick={this.handleUpdateName.bind(this)}>修改值</button>
      </div>
    );
  }
}

複製程式碼

最後說一點,就是setState是淺拷貝,如果是結構比較深層的物件,很多同學會採用JSON.string()這種來進行深拷貝,這樣的操作雖然說是可以的,但是非常影響效能。

React推出了immutable與15版本之後推出來的PureComponent就是為了解決這些問題的。有興趣的同學可以搜尋一下相關資料進行擴充閱讀。

3.2 什麼是props

我們知道,在函式中有帶引數的方法,那麼props其實就是傳入方法中的引數。並且在React中props是隻讀屬性。在使用場景上,是由父元件向子元件傳遞值的時候使用的。

3.2.1 接收並渲染props值

我們首先建立一個可以接收props值的元件。

import React, { PureComponent } from "react";

export default class index extends PureComponent {
  constructor(props) {
    super(props);
  }
  
  render() {
    return (
      <div style={{'background':'#fefefe'}}>
        {this.props.value||'暫無資料'}
      </div>
    );
  }
}
複製程式碼

接著,我們修改Index.js引用這個元件。

import React, { PureComponent } from "react";
import Content from './content.js'
export default class index extends PureComponent {
  constructor(props) {
    super(props);
    this.state = { name: "demo react", time: +new Date() };
  }
  handleUpdateName() {
    this.setState({time:+new Date()})
  }
  render() {
    return (
      <div>
        Hello world React!{this.state.name}
        <p>元件生成時間:{this.state.time}</p>
        <button onClick={this.handleUpdateName.bind(this)}>修改值</button>
        <Content/>
      </div>
    );
  }
}

複製程式碼

這裡大家有一點要注意,在React中,所有的元件名稱第一個字母都必須大寫,這是為了與html標籤區分出來。

如何向子元件傳值呢?就像給html標籤增加屬性一樣。

<Content value={'我設定了' + this.state.time}/>
複製程式碼

這樣,元件內部可以通過props讀取到value值了。不過React的元件傳遞中有一個很有趣的屬性children,這個的用處傳遞元件包含的內容。

繼續修改引入元件的程式碼,如下

// index.js
<Content value={'我設定了' + this.state.time} >主體Children</Content>
複製程式碼
import React, { PureComponent } from "react";

export default class index extends PureComponent {
  constructor(props) {
    super(props);
  }
  
  render() {
    return (
      <div style={{'background':'#fefefe'}}>
        {this.props.value||'暫無資料'},children:{this.props.children}
      </div>
    );
  }
}

複製程式碼

最終顯示效果就是這樣的

Alt text

GitHub程式碼地址:github.com/yodfz/learn…

原文地址:www.yodfz.com/detail/36/3…

相關文章