其中涉及到 React 組件復(fù)用,、輪子設(shè)計(jì)相關(guān)話題,,并配合相關(guān)場(chǎng)景實(shí)例進(jìn)行了分析,。這些內(nèi)容都算是 React 設(shè)計(jì)模式,,一提到 Design Patterns,,讀者大可不必恐懼,事實(shí)上這都是 React 開(kāi)發(fā)應(yīng)用靈活性的體現(xiàn),。今天這篇文章,,我們繼續(xù)通過(guò)一個(gè)場(chǎng)景,循序漸進(jìn),,通過(guò)一步步優(yōu)化設(shè)計(jì)來(lái)進(jìn)行加深理解,。 場(chǎng)景介紹屏幕左側(cè)大面積展現(xiàn)區(qū)塊內(nèi)容,點(diǎn)擊 continue 按鈕,,切換為下條內(nèi)容信息,;右側(cè)是一個(gè)導(dǎo)航條,指示當(dāng)前區(qū)塊展示信息條目,。 如果看 Gif 圖不過(guò)癮,,可以到 CodeSandbox 進(jìn)行在線了解。 具體代碼結(jié)構(gòu)為: class App extends Component { render() { return ( Stepper 組件 'stage' prop 表示默認(rèn)開(kāi)始第幾個(gè)區(qū)塊,,同時(shí)具用同名 'stage' 狀態(tài),。stage 在這里表示左側(cè)一個(gè)個(gè)內(nèi)容區(qū)塊。handleClick 方法對(duì) this.stata.stage 進(jìn)行切換,。 class Stepper extends Component { state = { stage: this.props.stage } static defaultProps = { stage: 1 } handleClick = () => { this.setState({ stage: this.state.stage + 1 }) } render() { const { stage } = this.state; return ( 我們看到,,Stepper 組件包含 Progress 組件(左側(cè)導(dǎo)航)以及 Steps 組件。這樣的代碼運(yùn)行良好,,但是在復(fù)用性和靈活性上有一些問(wèn)題,。比如:
現(xiàn)有代碼基礎(chǔ)上,這些問(wèn)題都可以解決,。但是需要重新更改組件編寫內(nèi)容,。如果某天又新增或者調(diào)整了需求,組件內(nèi)容同樣又需要改寫,。 接下來(lái),,我們用另一種方式實(shí)現(xiàn)需求,使得代碼更加靈活,,復(fù)用性更強(qiáng),。 重新設(shè)計(jì)仔細(xì)觀察 Stepper 組件:它包含了當(dāng)前區(qū)塊 stage,以及一個(gè)更改 stage 的方法,,渲染了兩個(gè)子組件,。 我們使用 Function as Child Component 手段,將 Stepper 組件重構(gòu),。(如果對(duì) Function as Child Component 不熟悉,,請(qǐng)參考我之前文章 組件復(fù)用那些事兒 - React 實(shí)現(xiàn)按需加載輪子) 如下圖: Progress 和 Steps 組件不再直接出現(xiàn)在 Stepper 組件的 render 方法中。我們使用 this.props.children 對(duì) Stepper 組件的所有子組件進(jìn)行渲染,。這樣 Stepper 組件渲染的內(nèi)容更加靈活,。 但是僅僅這樣的修改是不可能完成需求的,當(dāng)用戶點(diǎn)擊 continue 按鈕,,stage 并不會(huì)進(jìn)行切換,。因?yàn)?Progress 和 Steps 組件無(wú)法再通過(guò) props 感知 stage 和 handleClick 方法。 為了解決這個(gè)問(wèn)題,,我們可以手動(dòng)遍歷 Stepper 組件的子節(jié)點(diǎn),,并對(duì)相應(yīng) props 一一注入。如下代碼: const children = React.Children.map(this.props.children, child => { return React.cloneElement(child, {stage, handleClick: this.handleClick}) }) 借助 React.Children.map 進(jìn)行子節(jié)點(diǎn)遍歷,,并通過(guò) React.cloneElement 方法對(duì)子組件進(jìn)行拷貝,,這個(gè)方法通過(guò)第二個(gè)參數(shù),具有添加額外 props 的能力,。Stepper 組件的 render 方法只需要具體應(yīng)用: const { stage } = this.state;const children = React.Children.map(this.props.children, child => { return React.cloneElement(child, {stage, handleClick: this.handleClick})})return ( 這樣一來(lái),,應(yīng)用又一次正確運(yùn)轉(zhuǎn)! class App extends Component { render() { return ( 同樣的手段,,我們也可以應(yīng)用到 Progress 組件當(dāng)中,。這里不再一一展開(kāi)。 使用 Static Properties值得一提的是,,我們可以使用 Static Properties 增強(qiáng)代碼的可讀性,。Static Properties 允許我們?cè)?class 當(dāng)中直接對(duì)方法進(jìn)行調(diào)用,。首先,我們?cè)?Stepper 組件中創(chuàng)建兩個(gè) static 方法,,并賦值給 Progress 組件和 Steps 組件: static Progress = Progress;static Steps = Steps 現(xiàn)在,,在 App.js 中我們可以直接: import React, { Component } from 'react';import Stepper from './Stepper'class App extends Component { render() { return ( 這樣的好處體現(xiàn)在不用一次次地 import 進(jìn)來(lái) Progress 組件和 Steps 組件,它們都將作為 Stepper 的靜態(tài)屬性出現(xiàn),。我個(gè)人并不是很喜歡這種做法。 使用 React Transition Group我們使用 React Transition Group 對(duì) Steps 組件內(nèi)容添加過(guò)渡動(dòng)畫(huà),。只有當(dāng) props.num 與 this.props.stage 相等時(shí),,區(qū)塊內(nèi)容設(shè)置為可見(jiàn): class Steps extends Component { render() { const {stage,handleClick} = this.props const children = React.Children.map(this.props.children, child => { console.log(child.props) return ( stage === child.props.num && 我們也可以給 Steps 組件添加任意個(gè)內(nèi)容: import Stepper from './Stepper'class App extends Component { render() { return ( 重新設(shè)計(jì)之后,整個(gè)應(yīng)用變得更加靈活,,復(fù)用性更強(qiáng),。我們可以指定任意個(gè) stages,每一個(gè) stage 文本內(nèi)容也可以自定義設(shè)置,,同樣 stages 排列順序等都可以隨意搭配,。 重構(gòu)代碼以及效果可以訪問(wèn)這里查看。 思考及待續(xù)如果你覺(jué)得上述代碼完美無(wú)懈可擊,,那顯然想簡(jiǎn)單了,。需求是變化多端的,如果我們想在 Steps 區(qū)塊上,,加一個(gè)大標(biāo)題呢,? class App extends Component { render() { return ( 如圖, 這樣一來(lái),,Stepper.Steps 組件再也不是 Stepper 組件的直接唯一子節(jié)點(diǎn)了,,那預(yù)期之中的 props 自然又一次無(wú)法取得! 問(wèn)題也不僅僅于此,。筆者本人不是很喜歡類似 React.cloneElement 頂層 API,,除了偏好以外,也有一個(gè)難以規(guī)避的問(wèn)題:在使用 React.cloneElement 擴(kuò)充 props 時(shí),,如果出現(xiàn) props 命名沖突怎么辦,? 比如一個(gè) input 遇見(jiàn)了命名為 value 的 prop,后果可想而知,。 那么問(wèn)題來(lái)了,,是否有更優(yōu)雅高效的方法解決上述問(wèn)題?或者,,是否有更好的方式,,實(shí)現(xiàn)更靈活的設(shè)計(jì)? |
|