本系列目錄
點贊是美德 : )
React 快速上手 - 06 元件設計 容器元件、展示元件、操作元件
本文主要思考的問題是如何設計元件,react 庫設計的那麼靈巧,肯定不是用來做笨重的東西~
我將會用 截圖 + 簡述文字 + 精簡程式碼
來說明
目標
瞭解元件設計幾個參考點:
- 元件拆封原則
- 元件間通訊
- 雙向繫結
1. 元件設計
1.1 按有無 狀態管理
可以分為 有狀態元件
無狀態元件
圖解說明
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 按元件 職能
劃分為 容器元件
操作元件
展示元件
圖解說明
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>
)
}
}
複製程式碼
handleSync
中 form[item.name] = item.value
動態更新 key / value
達到更新 state
- 動圖效果
- codepen
https://codepen.io/ducafecat/pen/GdBPZZ
建議
通過學習本章後,大家寫具體功能程式碼前,可以先做下 UI元件架構設計
這個沒有那麼神祕,就是描述下有哪些元件、他們之間如何組裝
如果大腦中抽象的不清楚,可以藉助原型工具設計,自己能看懂就行,否則邊寫邊設計容易亂掉
設計完成後,過幾遍沒啥問題了,再編寫具體功能