React Component存在的幾種形式詳解
前言
最近項(xiàng)目基本都是用 React,今天總結(jié)分享 React Component 常見的幾種形式,如果你在寫 React 時(shí)經(jīng)常不知道怎么拆分代碼,這篇文章或許對(duì)你有所幫助。
React.Component是一個(gè)抽象基類。這意味著直接引用React.Component是毫無(wú)意義的。你可以實(shí)現(xiàn)一個(gè)它的子類,并且至少定義一個(gè)render()方法即可使用。
為了更充分理解 React,先搞懂平時(shí)寫的 JSX 是什么。初學(xué)的時(shí)候有比較大困惑,這是一門新語(yǔ)言嗎?大部分人是匆匆掃過(guò)文檔就開始開發(fā)。通過(guò) babel-presets-react 處理能看到,其實(shí) JSX 只是語(yǔ)法糖,最終在瀏覽器跑的還是 JS。React Component 最終都通過(guò) React.createElement 創(chuàng)建。 總之,寫 React 其實(shí)就是在寫 JS 。

SFC (Stateless Functional Component)
React 可以使用 Function 來(lái)創(chuàng)建 Component,這類 Component 沒有 lifecycle, 內(nèi)部不維護(hù) state,只要傳入的 props 有變化則進(jìn)行重新渲染。
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
用箭頭函數(shù)的寫法還更加簡(jiǎn)潔。
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,代碼簡(jiǎn)短沒有其他條件分支,并且相比 class Component 編譯后的代碼量會(huì)少一些。
尷尬的是,在 React 16.7 react hooks 出來(lái)之后,SFC 這個(gè)名字有歧義了,因?yàn)橛蒙?useState,SFC 也可以有 local state, 同樣可以擁有 lifecycle。再稱之為 Stateless Components 就很尷尬,改名叫 FC ?
HOC (Higher-Order Components)
高階組件對(duì)于 Vue 開發(fā)者來(lái)說(shuō)應(yīng)該是個(gè)陌生的概念(不知道,我用 Vue 的時(shí)候沒見過(guò)類似的用法)。從代碼上看,高階組件就是一個(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 方法,通過(guò)傳入 組件和 map*ToProps 方法,讓組件和 store 連接。組件內(nèi)部就可以直接通過(guò) props 獲得 connect 之后的值。
exprot default connect( mapStateToProps, mapDispatchToProps, )(Component);
高階組件適合用來(lái)擴(kuò)展功能,把這部分功能從業(yè)務(wù)組件中抽離出來(lái),需要的套上,不需要的時(shí)候移除,對(duì)被包裹組件侵入性非常小。
Dynamic Component
有些業(yè)務(wù)場(chǎng)景下,在執(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ù)萬(wàn)物皆為 JS 理論,只要傳入不同的 tag 標(biāo)簽,就會(huì)渲染出不同的 heading 標(biāo)簽。

我們常用這種方式,在后端配置組件和數(shù)據(jù),前端讀取配置之后渲染出對(duì)應(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)營(yíng)配置倒計(jì)時(shí)結(jié)束時(shí)間,倒計(jì)時(shí)初始化時(shí)間從服務(wù)端獲取,結(jié)束之前顯示倒計(jì)時(shí),倒計(jì)時(shí)結(jié)束之后做對(duì)應(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組件,對(duì)剩余時(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)。
- 單元測(cè)試簡(jiǎn)單,每個(gè)組件都只測(cè)試自身的邏輯。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
React中g(shù)etDefaultProps的使用小結(jié)
React中的getDefaultProps功能允許開發(fā)者為類組件定義默認(rèn)屬性,提高組件的靈活性和容錯(cuò)性,本文介紹了getDefaultProps的作用、語(yǔ)法以及最佳實(shí)踐,并探討了其他替代方案,如函數(shù)組件中的默認(rèn)參數(shù)、高階組件和ContextAPI等,理解這些概念有助于提升代碼的可維護(hù)性和用戶體驗(yàn)2024-09-09
React中Provider組件詳解(使用場(chǎng)景)
這篇文章主要介紹了React中Provider組件使用場(chǎng)景,使用Provider可以解決數(shù)據(jù)層層傳遞和每個(gè)組件都要傳props的問題,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-02-02
TypeScript在React項(xiàng)目中的使用實(shí)踐總結(jié)
這篇文章主要介紹了TypeScript在React項(xiàng)目中的使用總結(jié),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04
React項(xiàng)目中使用zustand狀態(tài)管理的實(shí)現(xiàn)
zustand是一個(gè)用于狀態(tài)管理的小巧而強(qiáng)大的庫(kù),本文主要介紹了React項(xiàng)目中使用zustand狀態(tài)管理的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2023-10-10
react hooks使用Echarts圖表中遇到的情況及相關(guān)配置問題
這篇文章主要介紹了react hooks使用Echarts圖表中遇到的情況及相關(guān)配置問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03

