組件是 React 的核心,因此了解如何利用它們對於創建優秀的設計結構至關重要。
什么是組件
根據 React 官網的介紹,“組件讓你可以將 UI 分割成獨立的、可重用的部分,並獨立管理每個部分。”
當你第一次安裝 npm install react
時,會得到一件事:組件及其 API。與 JavaScript 函數類似,組件接受名為 “props” 的輸入並返回 React 元素,該元素描述(聲明)用戶界面(UI)的外觀。這就是為什么 React 被稱為聲明性 API,因為你告訴它你希望 UI 是什么樣子的,而 React 負責其余的工作。
可以把聲明式想像成當打的去一個目的地時,只需要告訴司機去哪里,他就會開車把你送到那里。命令式編程正好相反—,你得自己駕車到那里。
組件的 API
當安裝 React 后,便可以使用 React 提供的 API,基本可以分成 5 種。
- render
- state
- props
- context
- lifecycle events
盡管一個組件可以使用上述所有 API,但一個組件通常用到只有少數幾個 API,而其他組件則只使用其他 API。
可以對組件使用不同的 API 對組件進行划分,分為 有狀態(stateful) 和 無狀態(stateless) 兩種組件。
- 有狀態組件通常使用 API: render, state 和生命周期相關事件。
- 無狀態組件通常使用 API: render, props 和 context。
以上就是我們為佬要介紹 組件模式 的原因。組件模式是使用 React 時的最佳實踐,最初引入組件模式是為了將數據邏輯和 UI 表現層進行分離。通過在組件之間划分職責,您以創建更多可重用的、內聚的組件,這些組件可用於組合復雜的 UI,這在構建可擴展的應用程序時尤其重要。
組件模式
通常組件模式有以下幾種:
- Container (容器組件)
- Presentational (展示組件)
- Higher order components (高級組件)
- Render callback (渲染回調)
代碼部署后可能存在的BUG沒法實時知道,事后為了解決這些BUG,花了大量的時間進行log 調試,這邊順便給大家推薦一個好用的BUG監控工具 Fundebug。
Container (容器組件)
“容器組件就是取數據,然后渲染子組件而已” —— Jason Bonta
容器組件是你的數據或邏輯層並利用 stateful API,使用生命周期事件,你可以連接 state
到
redux
或者 Flux
的 storage
中,並將數據和回調作為 props
傳遞給子組件。
在容器組件的 render
方法中,你可以使用 展示組件 來渲染具體的樣式。為了能夠訪問到所有狀態 API,容器組件必須使用 class
的方式聲明,而不是使用函數式方法聲明。
在下面的示例中,我們有一個名為 Greeting
的類組件,它具有狀態,生命周期事件componentDidMount()
和 render
方法。
class Greeting extends React.Component {
constructor() {
super();
this.state = {
name: "",
};
}
componentDidMount() {
// AJAX
this.setState(() => {
return {
name: "William",
};
});
}
render() {
return (
<div>
<h1>Hello! {this.state.name}</h1>
</div>
);
}
}
此時,該組件是一個有狀態類組件,為了使 Greeting
成為一個容器組件,我們可以將 UI 拆分為一個 展示組件,將在下面進行說明。
展示組件
展示組件 使用 props
、render
和 context
(無狀態API),並且由於不需要使用生命周期相關 Api,可以使用純函數來簡化表述它們:
const GreetingCard = (props) => {
return (
<div>
<h1>Hello! {props.name}</h1>
</div>
)
}
展示組件 僅從 props
接收數據和回調,這些數據和回調可以由其容器組件(父組件)提供。
容器組件和展示組件各自將數據/邏輯和展示部分封裝到各自的組件中:
const GreetingCard = (props) => {
return (
<div>
<h1>{props.name}</h1>
</div>
)
}
class Greeting extends React.Component {
constructor() {
super();
this.state = {
name: "",
};
}
componentDidMount() {
// AJAX
this.setState(() => {
return {
name: "William",
};
});
}
render() {
return (
<div>
<GreetingCard name={this.state.name} />
</div>
);
}
}
如你所見,已經將 Greeting
組件中展示相關的部分移動到了它自己的函數式展示組件中。當然,這是一個非常簡單的例子——對於更復雜的應用程序,這也是最基本的。
高階組件
高階組件是一種函數,它接受一個組件作為參數,然后返回一個新的組件。
這是一種可以對輸入組件的 props
進行修改(增刪改查)然后返回全新的修改后的組件強大模式,想想 react-router-v4 和 redux 。用了 react-router-v4 后,你可以使用 withRouter() 來繼承以 props
形式傳遞給組件的各種方法。同樣,用了redux
,就可以使用 connect({})()
方法來將展示組件和 store
中的數據進行連接。
代碼演示:
import {withRouter} from 'react-router-dom';
class App extends React.Component {
constructor() {
super();
this.state = {path: ''}
}
componentDidMount() {
let pathName = this.props.location.pathname;
this.setState(() => {
return {
path: pathName,
}
})
}
render() {
return (
<div>
<h1>Hi! I'm being rendered at: {this.state.path}</h1>
</div>
)
}
}
export default withRouter(App);
導出組件時,使用用 react-router-v4 的 withRouter()
方法封裝它。 在 組件 App 的生命周期事件 componentDidMount()
方法中,我們使用this.props.location.pathname
提供的值來更新 state
。 由於我們使用了 withRouter
高階組件,我們可以直接訪問 this.props.locationlocation
,而不需要直接將 location
作為 props
直接傳入,非常方便。
渲染回調
與高階組件類似,渲染回調或渲染 props
被用於共享或重用組件邏輯。雖然許多開發人員傾向於使用 高階組件 的可重用邏輯,但是使用 渲染回調 仍然有一些非常好的理由和優勢——這是在 Michael Jackson 的“永不寫另一個高階組件”中得到了最好的解釋。簡而言之,渲染回調減少了命名空間沖突,並更好的說明了邏輯來源。
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
};
}
increment = () => {
this.setState(prevState => {
return {
count: prevState.count + 1,
};
});
};
render() {
return (
<div onClick={this.increment}>{this.props.children(this.state)}</div>
);
}
}
class App extends React.Component {
render() {
return (
<Counter>
{state => (
<div>
<h1>The count is: {state.count}</h1>
</div>
)}
</Counter>
);
}
}
在 Counter
類中,在 render
方法中嵌入 this.props.children
並將 this.state
作為參數。在 App
類中,我們可以將我們組件封裝在 Counter
組件中,因此我可以操作 Counter
組件內的邏輯。
Counter
組件的本質是暴露了 children
這個外部屬性,將 children
具體的渲染細節交個 Counter
的使用者,使用的時候只需要將組件傳入到 Counter
的 children
中,當然可以使用其他參數,如果 children
不夠的話。
關於Fundebug
Fundebug專注於JavaScript、微信小程序、微信小游戲、支付寶小程序、React Native、Node.js和Java線上應用實時BUG監控。 自從2016年雙十一正式上線,Fundebug累計處理了10億+錯誤事件,付費客戶有Google、360、金山軟件、百姓網等眾多品牌企業。歡迎大家免費試用!