React 快速上手 - 06 元件設計 容器元件、展示元件、操作元件

會煮咖啡的貓發表於2018-05-23

react

本系列目錄

點贊是美德 : )


React 快速上手 - 06 元件設計 容器元件、展示元件、操作元件

本文主要思考的問題是如何設計元件,react 庫設計的那麼靈巧,肯定不是用來做笨重的東西~

我將會用 截圖 + 簡述文字 + 精簡程式碼 來說明

目標

瞭解元件設計幾個參考點:

  • 元件拆封原則
  • 元件間通訊
  • 雙向繫結

1. 元件設計

1.1 按有無 狀態管理 可以分為 有狀態元件 無狀態元件

圖解說明

react-有無狀態管理

1.1.1 有狀態元件, 用 React.Component 形式建立

  • 用來管理應用資料,如業務上,首頁需要資料有:
    • 推薦分類
    • 推薦廣告
    • 推薦商品

程式碼示例

class MyComponent extends Component {
  constructor(props) {
    super(props)
    this.state = {
      推薦分類列表:[],
      推薦廣告列表:[],
      推薦商品列表:[]
      }
  }
  render() {
    return <首頁>...</首頁>
  }
}
複製程式碼

1.1.2 無狀態元件,以函式的方式展現多,在開發中儘量用這種形式

這種元件自己不維護 狀態,資料靠屬性傳入

程式碼示例

function Element(props) {
  let val = props.val
  return <div>元件 - ...</div>
}
複製程式碼

1.2 按元件 職能 劃分為 容器元件 操作元件 展示元件

圖解說明

react-按職能劃分元件

1.2.1 容器元件

這類元件本身是一個有狀態元件,像一個舞臺把大家都呈現上去, 把資料用 屬性 方式傳下去,然後子元件用 事件 方式把資料返回

  • 像上面這個商品搜尋業務
    • 狀態有: 搜尋關鍵字、排序、商品列表
    • 子元件有: 搜尋欄、列表控制欄、商品列表

程式碼示例

class Container extends Component {
  constructor(props) {
    super(props)
    this.state = {
      搜尋關鍵字: '手機殼',
      排序: '綜合',
      商品列表: []
      }
  }
  render() {
    return (
      <div>
        <搜尋欄 關鍵字={this.state.搜尋關鍵字} onChange={...} />
        <列表控制欄 排序={this.state.排序} onChange={...} />
        <商品列表 商品列表={this.state.商品列表} onChange={...} />
      </div>
    )
  }
}
複製程式碼

1.2.1 操作元件

處理互動操作,比如 搜尋框、使用者註冊登入、購物車、文章編輯、拍圖、上傳

圖解中的搜尋框,接收 屬性 關鍵字 產生的新資料 事件 方式返回容器元件

程式碼示例

function SearchInput(props) {
  let 關鍵字 = props.關鍵字
  return (
    <div>
      <input
        type="text"
        value={關鍵字}
        onKeyDown={props.onChange}
        placeholder="輸入 ...... ↩"
      />
    </div>
  )
}
複製程式碼

1.2.1 展示元件

這種就更純粹了,只接收 屬性 資料,用來展示

圖解中的商品列表,接收 屬性 商品列表

程式碼示例

function SearchInput(props) {
  let 商品列表 = props.商品列表
  return (
    <div>
      {商品列表.map((item, index) => (
        <商品項 key={item.id} 商品資訊={item} />
      ))}
    </div>
  )
}
複製程式碼

其中 商品資訊 元件也是一個 展示元件, 儘可能的把元件拆分

2. 元件間通訊

其實就是在一個容器元件上,擺放了一個 控制元件 和一個 展示元件

圖解說明

元件間通訊

我們動手寫一下

第一步: 編寫輸入元件

程式碼

function InputView(props) {
  return (
    <div>
      <input
        type="text"
        onKeyDown={props.onChange}
        placeholder="輸入 ...... ↩"
      />
    </div>
  )
}
複製程式碼

處理 onKeyDown 訊息,返回給父容器

第二步: 編寫列表展示元件

程式碼

function ListView(props) {
  return (
    <ol>
      {props.datas &&
        props.datas.map((item, index) => (
          <li key={index.toString()}>{item}</li>
        ))}
    </ol>
  )
}
複製程式碼

map 迴圈列印資料列表

第三步: 容器元件繫結狀態、事件

程式碼

class ContainerView extends Component {
  constructor(props) {
    super(props)
    this.state = {list: []}
    this.handleChange = this.handleChange.bind(this)
  }
  handleChange(e) {
    if (e.keyCode === 13) {
      const value = e.target.value
      e.target.value = ''
      this.setState((state, props) => {
        let list = state.list
        list.push(value)
        return {list}
      })
    }
  }
  render() {
    return (
      <div>
        <InputView onChange={this.handleChange} />
        <ListView datas={this.state.list} />
      </div>
    )
  }
}
複製程式碼

e.keyCode === 13 表示一直監控到輸入回車,開始更新狀態

  • 動圖效果

元件間通訊

  • codepen

https://codepen.io/ducafecat/pen/pVZqjp

3. 資料雙向繫結

這個例子加入資料雙向繫結功能,這在表單操作中用的很頻繁

圖解說明

資料雙向繫結

還是用程式碼說明

3.1 第一步:輸入元件

程式碼

class InputView extends Component {
  constructor(props) {
    super(props)
    this.form = props.form // 父容器 state.form
    this.sync = props.sync // 父容器 sync
    this.handleChange = this.handleChange.bind(this)
  }
  handleChange(e) {
    let name = e.target.attributes.name.value
    let value = e.target.value
    this.sync({name, value})
  }
  render() {
    return (
      <ul>
        <li>
          <input
            name="input"
            type="text"
            value={this.form.input}
            onChange={this.handleChange}
          />
        </li>
        <li>
          <textarea
            name="textarea"
            value={this.form.textarea}
            onChange={this.handleChange}
          />
        </li>
        <li>
          <select
            name="select"
            value={this.form.select}
            onChange={this.handleChange}
          >
            <option value="">---</option>
            <option value="grapefruit">Grapefruit</option>
            <option value="lime">Lime</option>
            <option value="coconut">Coconut</option>
            <option value="mango">Mango</option>
          </select>
        </li>
      </ul>
    )
  }
}
複製程式碼
  • props.form 是容器傳入的表單資料,結構如下
{
  input: '',
  textarea: '',
  select: ''
}
複製程式碼

按控制元件名稱 key / val 結構

  • props.sync 是回傳父容器的事件,相應程式碼如下
  handleChange(e) {
    let name = e.target.attributes.name.value
    let value = e.target.value
    this.sync({name, value})
  }
複製程式碼

可以發現回傳的是 {控制元件名, 控制元件值},這裡是簡寫(鍵、值 名相同時可以寫一個),完整格式是

{
  name: name,
  value: value
}
複製程式碼

3.2 第二步:展示元件

程式碼

function ListView(props) {
  let form = props.form
  let list = []
  for (let key in form) {
    list.push({
      key,
      value: form[key]
    })
  }
  return (
    <ul>
      {list &&
        list.map((item, index) => (
          <li key={index.toString()}>
            {item.key} - {item.value}
          </li>
        ))}
    </ul>
  )
}
複製程式碼

這裡做展示就簡單了,接收到屬性 form 後,格式化成陣列 list ,然後 map 列印

3.3 第三步:容器元件

程式碼

class ContainerView extends Component {
  constructor(props) {
    super(props)
    this.state = {form: {input: '', textarea: '', select: ''}}
    this.handleSync = this.handleSync.bind(this)
  }
  handleSync(item) {
    this.setState((prevState, props) => {
      let form = prevState.form
      form[item.name] = item.value
      return {form}
    })
  }
  render() {
    return (
      <div>
        <InputView sync={this.handleSync} form={this.state.form} />
        <ListView form={this.state.form} />
      </div>
    )
  }
}
複製程式碼

handleSyncform[item.name] = item.value 動態更新 key / value 達到更新 state

  • 動圖效果

React 快速上手 - 06 元件設計 容器元件、展示元件、操作元件

  • codepen

https://codepen.io/ducafecat/pen/GdBPZZ

建議

通過學習本章後,大家寫具體功能程式碼前,可以先做下 UI元件架構設計

這個沒有那麼神祕,就是描述下有哪些元件、他們之間如何組裝

如果大腦中抽象的不清楚,可以藉助原型工具設計,自己能看懂就行,否則邊寫邊設計容易亂掉

設計完成後,過幾遍沒啥問題了,再編寫具體功能

程式碼

參考文

相關文章