組件化開發是React的核心,學會如何利用和組織他們有助於你建立良好的設計結構。
什麼是組件?
根據react官網的解釋大致如下:
組件可以獨立的拆分你的UI視圖,他們是可以複用的,獨立的單元。
和函數類似,React中組件接收的輸入源稱為props,並且返回react元素。react元素是對UI介面的描述。你可以告訴react你期望的UI介面,剩下的工作可以交給react來處理。
我們拿乘坐計程車打比方,當你告訴司機你想要去的地方,司機就會按照你期望地點將你安全的送到目的地。而不用你自己親自開車。
Component Api's
react的api有哪些呢?react總共有主要的5大api他們分別是:
1.render
2.state
3.props
4.context
5.life cycle events
組件有狀態組件和無狀態組件,狀態組件使用可控制狀態的api,render state 和life cycle events。無狀態組件使用render props context。
組件設計模式,可以將你的資料層和邏輯層分開,通過職責區分你的組件。你可以建立可以複用的組件,而這些組件又可以建立出更加複雜的UI試圖。這對於構建app是極為重要的。
在組件設計模式中我們可以將組件分為:
1.容器組件
2.展示型組件
3.高階組件
4.render回調
容器組件:
容器組件用於擷取資料,渲染他下面的子組件,這個就是容器組件。
容器組件可以是你的資料層或者商務邏輯層,並且使用的是stateful api。通過生命週期函數你可以串連你的狀態管理庫,例如redux或者flux。容器組件可以傳遞資料和回呼函數給子組件。在容器組件的render方法中,通過展示組件的組合來構建你的UI試圖。因為容器組件是具有狀態的,所以你需要使用class來聲明你的組件,而不是使用函數式組件。
在如下例子中,有個class component名為Greeting,他有state,生命週期函數和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> ); }}
這個組件是具備狀態的class組件。為了讓Greeting組件成為容器型組件,我們需要把UI拆分到展示型組件中。如下:
展示型組件:
展示型組件利用的是,props,render,context(stateless api's)
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> ); }}
如上樣本,我們移除了標籤元素,並且將他放入了無狀態的展示型組件中。當然這個只是這個很簡單的樣本,但是在複雜的app運用中,原理都是相同的。
高階組件:
高階組件,是以組件作為參數,並且返回一個新的組件。
高階組件可以提供資料給任意數量的組件,並且有效複用商務邏輯。它是個強大的模式。例如在react-router和redux中,在react-router中你可以使用widthRouter()去繼承方法,並且通過props傳遞給你的組件。在redux中你可以connect方法去傳遞你的actions。
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);
如上樣本,我們通過this.props.location.pathname來更新狀態。通過withRouter方法來包裹組件。現在我們的組件可以通過props來出來react-rouoter的方法this.props.location.pathname;
Render callbacks
類似於高階組件,render callbacks或者render props都是用來複用組價邏輯的。當更多的開發人員傾向於高階組件來複用商務邏輯的時候,這裡有幾個原因和優點可以讓你嘗試下使用render callbacks。render callsbacks可以有效減少命名衝突,並且清晰的展示邏輯的來源。
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中的state。