Day04: state 及 props 介紹


父組件與子組件根本就是現代社會家庭的寫照

要提到 React 的 stateprops 前,應該要先講一個觀念,子組件與父組件的設計:

class Counter extends Component {
  render(){
    return (
      <div>
        <button>-1</button>
        <h1>1</h1>
        <button>+1</button>
      </div>
    )
  }
}


class App extends Component {
  state = {
    name: 'Frank'
  }
  render() {
    return (
      <div className="App">
      <header className="App-header">
        <Counter/>
      </header>
    </div>
    );
  }
}

先嘗試弄個計數器試試看,背景我採用原本 create-react-app。
請注意:Component 的命名開頭要大寫,而 tag 屬性採駝峰式命名。

從程式碼可以看出,App 中有一個 Counter tag,也就是在 App 裡面加入了一個 Counter 組件,而這樣的關係我們稱 App 為父組件,Counter 為子組件,既然有這樣的關係,是必須要一些父子溝通,stateprops 就是扮演溝通很重要的腳色,先從 state 開始吧。

state

state 可以掌握一個組件的狀態,也可以透過 state 的值來控制 UI 會顯示的值。

class Counter extends Component {
  state = {
    count: 1
  }

  render(){
    return (
      <div>
        <button>-1</button>
        <h1>{this.state.count}</h1>
        <button>+1</button>
      </div>
    )
  }
}

這邊就不去用 css 去調整版型了,若要使用 css,可以 import css 檔在 src 目錄中。

state 以物件方式呈現,裡面可以包著很多狀態,對應到 UI 上,不過這也只能是把值綁定到 h1 中,要怎麼去改它的值?在原本 JavaScript 的做法會是寫 onClick 的監聽事件,在 React 也可以用 onClick 的方式帶入 Function,再透過 setState 的方式變更值:

class Counter extends Component {
  state = {
    count: 1
  }

  addCount = () => {
    this.setState({
      count: this.state.count + 1
    })
  }

  minusCount = () => {
    this.setState({
      count: this.state.count - 1
    })
  } 

  render(){
    return (
      <div>
        <button onClick={this.minusCount}>-1</button>
        <h1>{this.state.count}</h1>
        <button onClick={this.addCount}>+1</button>
      </div>
    )
  }
}

這邊有幾個重點需要整理:

  1. setState 的用法是把一個也像 state 的物件,包起來再做想要做的運算。
  2. 這邊使用的是箭頭函式的 babel plugin,因此 button 裡的 this 會指向 Component,而不會指向 button 本身。
  3. 雖然函式名稱不用駝峰式命名還是可以使用,但還是有個習慣比較好,畢竟像是 onClick 就必須使用這樣命名(我也是寫 React 才養成這習慣)。
  4. 請記得:setState 是一個非同步函式,另外每當觸發 setStatecomponent 會重新 render

這樣已經完成一個簡單的計數器,而還可以再將它優化。

  render(){
    const {count} = this.state
    return (
      <div>
        <button onClick={this.minusCount}>-1</button>
        <h1>{count}</h1>
        <button onClick={this.addCount}>+1</button>
      </div>
    )
  }

加上解構賦值,當 state 東西一多、結構更繁雜時,不用每次都寫 this.state%&$#。

另外其實除了箭頭函式以外也有別種方式解決 this 的問題:

  render(){
    const {count} = this.state
    return (
      <div>
        <button onClick={this.minusCount.bind(this)}>-1</button>
        <h1>{count}</h1>
        <button onClick={this.addCount.bind(this)}>+1</button>
      </div>
    )
  }

透過 bind 綁定的方式直接將 this 指定給 Component

props

最一開始提到的父子組件溝通,嘗試了把計數器寫成一個 Counter 組件,既然都拆成兩個組件,是不是可以改寫成讓父組件控制子組件?透過 props 確實可以辦到,props 可以接受外來的組件給予的值,嘗試將子組件的 stateApp 去控制:

class Counter extends Component {
  render(){
    return (
      <div>
        <button>-1</button>
        <h1>{this.props.number}</h1>
        <button>+1</button>
      </div>
    )
  }
}


class App extends Component {
  state = {
    count: 1
  }
  render() {
    return (
      <div className="App">
      <header className="App-header">
        <Counter number={this.state.count}/>
      </header>
    </div>
    );
  }
}

將子組件 Counterstate.count 交給父組件管,就像爸爸管兒子的零用錢一樣。

除了 state,我們也可以把 buttononClick 事件傳進去:

class Counter extends Component {
  render(){
    return (
      <div>
        <button onClick={this.props.minus}>-1</button>
        <h1>{this.props.number}</h1>
        <button onClick={this.props.add}>+1</button>
      </div>
    )
  }
}


class App extends Component {
  state = {
    count: 1
  }
  addCount = () => {
    this.setState({
      count: this.state.count + 1
    })
  }

  minusCount = () => {
    this.setState({
      count: this.state.count - 1
    })
  } 
  render() {
    return (
      <div className="App">
      <header className="App-header">
        <Counter number={this.state.count} 
                 add={this.addCount} 
                 minus={this.minusCount}/>
      </header>
    </div>
    );
  }
}

從上可以發現 Counter 組件變得很簡潔,我在子組件它只有結構,事情都父組件做,子組件遵從父組件給予的值、Function 來回傳子組件結構。不過除了這寫法,也可以這樣寫:

class Counter extends Component {
  render(){
    return (
      <div>
        <button onClick={this.props.minus}>-1</button>
        <h1>{this.props.children}</h1>
        <button onClick={this.props.add}>+1</button>
      </div>
    )
  }
}


class App extends Component {
  state = {
    count: 1
  }
  addCount = () => {
    this.setState({
      count: this.state.count + 1
    })
  }

  minusCount = () => {
    this.setState({
      count: this.state.count - 1
    })
  } 
  render() {
    return (
      <div className="App">
      <header className="App-header">
        <Counter add={this.addCount} 
                 minus={this.minusCount}>{this.state.count}</Counter>
      </header>
    </div>
    );
  }
}

由於 tag 與 close tag 之間只有一個值,所以可以直接把 this.state.count 放進來,而在子組件端可以用 this.props.children 接收,這是 React 的內建用法。

總結

這篇提到了 stateprops 基本運用,可以發現 React 每個細節都有不同方法,我一開始在接觸時也是被搞得蠻亂的,下一篇會提關於 stateprops 的應用,我覺得能好好的使用 stateprops,對資料的基本掌握應該就沒什麼太大的問題了。

#React
React 七天寫作松
透過短短的七天,稍微把我對 React 的見解整理成七天份的文章,希望能對正在初學 React 的你有所幫助!






Related Posts

歡樂學 Python 位元組碼(byte code)

歡樂學 Python 位元組碼(byte code)

版本控制 - GitHub實作

版本控制 - GitHub實作

JS 模組化:Import 和 export

JS 模組化:Import 和 export

Day07 LIFF (LINE Front-end Framework)

Day07 LIFF (LINE Front-end Framework)

Day4 真不想承認啊,因為自己太過年輕所犯下的錯誤

Day4 真不想承認啊,因為自己太過年輕所犯下的錯誤

[Day05] Functor

[Day05] Functor



Comments