React Component存在的幾種形式詳解
前言
最近項(xiàng)目基本都是用 React,今天總結(jié)分享 React Component 常見的幾種形式,如果你在寫 React 時(shí)經(jīng)常不知道怎么拆分代碼,這篇文章或許對你有所幫助。
React.Component是一個(gè)抽象基類。這意味著直接引用React.Component是毫無意義的。你可以實(shí)現(xiàn)一個(gè)它的子類,并且至少定義一個(gè)render()方法即可使用。
為了更充分理解 React,先搞懂平時(shí)寫的 JSX 是什么。初學(xué)的時(shí)候有比較大困惑,這是一門新語言嗎?大部分人是匆匆掃過文檔就開始開發(fā)。通過 babel-presets-react 處理能看到,其實(shí) JSX 只是語法糖,最終在瀏覽器跑的還是 JS。React Component 最終都通過 React.createElement 創(chuàng)建。 總之,寫 React 其實(shí)就是在寫 JS 。
SFC (Stateless Functional Component)
React 可以使用 Function 來創(chuàng)建 Component,這類 Component 沒有 lifecycle, 內(nèi)部不維護(hù) state,只要傳入的 props 有變化則進(jìn)行重新渲染。
function Welcome(props) { return <h1>Hello, {props.name}</h1>; }
用箭頭函數(shù)的寫法還更加簡潔。
const Welcome = props => <h1>Hello, {props.name}</h1>;
上面兩種形式生成 es5 代碼都是一樣的。
var Welcome = function Welcome(props) { return _react2.default.createElement( "h1", null, "Hello, ", props.name ); };
SFC 的特點(diǎn)是純粹只做 render,代碼簡短沒有其他條件分支,并且相比 class Component 編譯后的代碼量會(huì)少一些。
尷尬的是,在 React 16.7 react hooks 出來之后,SFC 這個(gè)名字有歧義了,因?yàn)橛蒙?useState,SFC 也可以有 local state, 同樣可以擁有 lifecycle。再稱之為 Stateless Components 就很尷尬,改名叫 FC ?
HOC (Higher-Order Components)
高階組件對于 Vue 開發(fā)者來說應(yīng)該是個(gè)陌生的概念(不知道,我用 Vue 的時(shí)候沒見過類似的用法)。從代碼上看,高階組件就是一個(gè)方法,傳入一個(gè)組件,返回另一個(gè)組件。
function logProps(WrappedComponent) { return class extends React.Component { componentWillReceiveProps(nextProps) { console.log('Current props: ', this.props); console.log('Next props: ', nextProps); } render() { return <WrappedComponent {...this.props} />; } } }
最常見的高階組件是 react-redux 里面的 connect 方法,通過傳入 組件和 map*ToProps 方法,讓組件和 store 連接。組件內(nèi)部就可以直接通過 props 獲得 connect 之后的值。
exprot default connect( mapStateToProps, mapDispatchToProps, )(Component);
高階組件適合用來擴(kuò)展功能,把這部分功能從業(yè)務(wù)組件中抽離出來,需要的套上,不需要的時(shí)候移除,對被包裹組件侵入性非常小。
Dynamic Component
有些業(yè)務(wù)場景下,在執(zhí)行時(shí)才能確定具體的標(biāo)簽或者組件是什么。在 React 的世界里面,以大寫字母開頭會(huì)被當(dāng)成動(dòng)態(tài)組件加載,而小寫字母開頭會(huì)被認(rèn)為是 HTML DOM tag。
// Heading.js render() { const { tag: Tag, children } = this.props; return <Tag>{ children }</Tag> }
根據(jù)萬物皆為 JS 理論,只要傳入不同的 tag 標(biāo)簽,就會(huì)渲染出不同的 heading 標(biāo)簽。
我們常用這種方式,在后端配置組件和數(shù)據(jù),前端讀取配置之后渲染出對應(yīng)的內(nèi)容。
FaCC(Functions as Child Components)
React children 還可以是 Function 類型,如果直接調(diào)用它會(huì)什么寫法?
比如封裝一個(gè) Loading 組件,會(huì)給 children 提供 loading 參數(shù),業(yè)務(wù)組件再根據(jù) loading 判斷需要 render 什么內(nèi)容。
class LoadArea extends Component { state = { loading: true, }; componentDidMount() { asyncFunc() .then(() => { this.setState({ loading: false, }) }) .catch(() => { this.setState({ loading: false, }) }) } render() { return ( <React.Fragment> {this.props.children({ ...this.props, ...this.state, })} </React.Fragment> ); } }
用法
render() { <LoadingArea> ({ loading }) => { loading ? <Wating /> : <Main /> } </LoadingArea> }
同樣的,最終執(zhí)行時(shí)都是 JS,沒有什么好奇怪的。
React 16.* 新版本的 Conext.Consumer 就是采用了這種寫法。
render() { <ThemeContext.Provider value={this.state.theme}> ... <ThemeContext.Consumer> {({theme}) => ( <button style={{backgroundColor: theme.background}}> Toggle Theme </button> )} </ThemeContext.Consumer> ... </ThemeContext.Provider> }
再以最近開發(fā)的例子,分享組件拆分的好處。
需求:開發(fā)倒計(jì)時(shí)組件,運(yùn)營配置倒計(jì)時(shí)結(jié)束時(shí)間,倒計(jì)時(shí)初始化時(shí)間從服務(wù)端獲取,結(jié)束之前顯示倒計(jì)時(shí),倒計(jì)時(shí)結(jié)束之后做對應(yīng)的操作,比如切換倒計(jì)時(shí)為其他組件。
組件拆分:
- 一個(gè)業(yè)務(wù)層容器組件,負(fù)責(zé)統(tǒng)籌,處理業(yè)務(wù)邏輯。
- 一個(gè)通用‘倒計(jì)時(shí)'的組件,向服務(wù)端輪詢系統(tǒng)時(shí)間,計(jì)算當(dāng)前剩余時(shí)間,F(xiàn)aCC 的形式提供給 children。
- 一個(gè)倒計(jì)時(shí)UI組件,對剩余時(shí)間格式化以及 UI 展示。
偽代碼:
// CountDownContainer.js render() { const { endTime, renderSomethingAfterCountDown, } = this.props; return ( <TimeLeftProvider endTime={endTime} > {seconds => ( seconds > 0 ? <CountDown {...this.props} remainingSeconds={seconds} /> : renderSomethingAfterCountDown() )} </TimeLeftProvider> ); }
// TimeLeftProvider.js export default class TimeLeftProvider extends PureComponent { static propTypes = { children: PropTypes.func, endTime: PropTypes.number, } // ... componentDidMount() { this.poll(); } poll() { queryServerTime(); this.pollTimer = setInterval(() => { queryServerTime(); }, pollInterval * 1000); } countDown() { setInterval(() => { this.setState(prevState => ({ remainingSeconds: prevState.remainingSeconds - 1, })); }, 1000); } render() { const { remainingSeconds, reliable } = this.state; return this.props.children(remainingSeconds, reliable); } }
// CountDown.js function CountDown(props) { const { remainingSeconds, } = props; const numbers = formatSeconds(remainingSeconds); const inputs = ['days', 'hours', 'minutes', 'seconds']; return ( <div styleName={cls}> { inputs.map(key => ({ label: key, number: numbers[key], })).map( //... ) } </div> ); }
最終得到的結(jié)果是:
與此同時(shí)
- 代碼結(jié)構(gòu)清晰,組件之間各司其職。
- 組件可復(fù)用性強(qiáng)。
- 單元測試簡單,每個(gè)組件都只測試自身的邏輯。
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
相關(guān)文章
React中g(shù)etDefaultProps的使用小結(jié)
React中的getDefaultProps功能允許開發(fā)者為類組件定義默認(rèn)屬性,提高組件的靈活性和容錯(cuò)性,本文介紹了getDefaultProps的作用、語法以及最佳實(shí)踐,并探討了其他替代方案,如函數(shù)組件中的默認(rèn)參數(shù)、高階組件和ContextAPI等,理解這些概念有助于提升代碼的可維護(hù)性和用戶體驗(yàn)2024-09-09TypeScript在React項(xiàng)目中的使用實(shí)踐總結(jié)
這篇文章主要介紹了TypeScript在React項(xiàng)目中的使用總結(jié),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04React項(xiàng)目中使用zustand狀態(tài)管理的實(shí)現(xiàn)
zustand是一個(gè)用于狀態(tài)管理的小巧而強(qiáng)大的庫,本文主要介紹了React項(xiàng)目中使用zustand狀態(tài)管理的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2023-10-10react hooks使用Echarts圖表中遇到的情況及相關(guān)配置問題
這篇文章主要介紹了react hooks使用Echarts圖表中遇到的情況及相關(guān)配置問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03