Component
React核心思想,一切皆是組件,通過組件可以達到最理想的代碼復用
栗子
import React from 'react'; export default class ContainerComponent extends React.Component { constructor(props) { super(props) this.state = { count: 0 } this.handlePlus = this.handlePlus.bind(this); } handlePlus() { this.setState({ count: this.state.count + 1 }) } render() { return <div> <h1>{`I am a container component.`}</h1> <p>{`當前計數:${this.state.count}`}</p> <button onClick={this.handlePlus}>plus</button> </div> } }
很簡單的一個組件,如果其他需要用,直接引用即可
import ContainerComponent from './ContainerComponent';
但是為了滿足最佳實踐,我們將該組件分成容器組件跟展示組件
容器組件:
import React from 'react'; import ShowComponent from './ShowComponent'; export default class ContainerComponent extends React.Component { constructor(props) { super(props) this.state = { count: 0 } this.handlePlus = this.handlePlus.bind(this); } handlePlus() { this.setState({ count: this.state.count + 1 }) } render() { return <div> <h1>{`I am a container component.`}</h1> <ShowComponent count={this.state.count} handlePlus={this.handlePlus} /> </div> } }
展示組件
export default ShowComponent; function ShowComponent(props) { return <div> <p>{`當前計數:${props.count}`}</p> <button onClick={props.handlePlus}>plus</button> </div> }
這里啥是容器組件,啥是展示組件?
容器組件:簡單理解就是含有處理邏輯,處理數據的容器
展示組件:只關心展示,跟數據無關,像控件,無狀態組件更貼切。
現在需求變了,如果展示組件不僅限於ShowComponent,現在多了ShowComponent2,showComponent3等等,怎么處理,噢?首先想到了什么?沒錯就是this.prop.children,
我們嘗試下。
children
栗子
ContainerComponent
import React from 'react'; export default class ContainerComponent extends React.Component { constructor(props) { super(props) } render() { return <div> <h1>{`I am a container component.`}</h1> {this.props.children} </div> } }
ShowComponent、ShowComponent2、ShowComponent3等等
export default ShowComponent; function ShowComponent(props) { return <div> <p>{`當前計數:${props.count}`}</p> <button onClick={props.handlePlus}>plus</button> </div> } class ShowComponent extends React.Component { constructor(props) { super(props); this.state = { count: 0 } this.handlePlus = this.handlePlus.bind(this); } handlePlus() { this.setState({ count: this.state.count + 1 }) } render() { return <div> <p>{`當前計數:${this.state.count}`}</p> <button onClick={this.handlePlus}>plus</button> </div> } }
可以實現我們的目的,但是缺點也比較明顯,就是沒辦法將容器組件與展示組件分開,如果想分開,我們需要在ShowComponent中進行分,其實不難看出,this.props.children更像是一種layout的解決方案,僅用來定制layout,具體邏輯放到每個具體Component中進行。那么怎么處理這種情況更好呢~我們來看看一個解決方案—>回調渲染。
回調渲染
什么是回調渲染呢,簡單來講,就是采用調用的形式使用children API,就是this.props.children(),看個例子
栗子
import React from "react"; import ReactDOM from "react-dom"; import ContainerComponent from "./ContainerComponent"; import ShowComponent from "./ShowComponent"; { ReactDOM.render( <ContainerComponent> {({ count, handlePlus }) => ( <ShowComponent count={count} handlePlus={handlePlus} /> )} </ContainerComponent>, document.getElementById("app") ); }
ContainerComponent
export default class ContainerComponent extends React.Component { constructor(props) { super(props) this.state = { count: 0 } this.handlePlus = this.handlePlus.bind(this); } handlePlus() { this.setState({ count: this.state.count + 1 }) } render() { return <div> <h1>{`I am a container component.`}</h1> { this.props.children( { count: this.state.count, handlePlus: this.handlePlus } ) } </div> } }
ShowComponent
export default ShowComponent; function ShowComponent(props) { return <div> <p>{`當前計數:${props.count}`}</p> <button onClick={props.handlePlus}>plus</button> </div> }
既可以達到ShowComponent與ContainerComponent解耦的目的(這里的意思是可以隨意更改ShowComponet,可以換成其他組件),又可以實現了展示組件與容器組件分離。這只是個demo
那么回調渲染到底解決了什么問題呢~回調渲染可以實現外層組件與內層組件的數據傳遞,可以很好的分離組件以及邏輯復用。
談到回調渲染,我們就會聯想到另一個方案,Render Props,那么什么是Render Props呢
Render Props
RenderProps我認為是回調渲染的一種衍生方式,利用一個函數,進行組件傳遞,這個組件就是可變的部分,其余部分實現代碼復用。我們試着用RenderProps方式對上個栗子進行改造,我們看個栗子。
栗子
import ContainerComponent from "./ContainerComponent"; import ShowComponent from "./ShowComponent"; { ReactDOM.render( <ContainerComponent render={({ count, handlePlus }) => ( <ShowComponent count={count} handlePlus={handlePlus} /> )} />, document.getElementById("app") ); }
ContainerComponent
import React from 'react'; import ShowComponent from './ShowComponent'; export default class ContainerComponent extends React.Component { constructor(props) { super(props) this.state = { count: 0 } this.handlePlus = this.handlePlus.bind(this); } handlePlus() { this.setState({ count: this.state.count + 1 }) } render() { return <div> <h1>{`I am a container component.`}</h1> { this.props.render({ count: this.state.count, handlePlus: this.handlePlus }) } </div> } }
ShowComponent
export default ShowComponent; function ShowComponent(props) { return <div> <p>{`當前計數:${props.count}`}</p> <button onClick={props.handlePlus}>plus</button> </div> }
其實跟回調渲染大同小異,回調渲染用的是React原生方法(this.props.children),RenderProps更靈活一些,可以定義多個函數,隨意傳遞,比如
ReactDOM.render( <ContainerComponent render={({ count, handlePlus }) => ( <ShowComponent count={count} handlePlus={handlePlus} /> )} renderMsg={() => <div>我是msg</div>} renderColor={() => <div>我是color</div>} />, document.getElementById("app") );
好的總結一下,RenderProps解決了什么問題?
同樣達到了代碼復用,組件拆解,降低耦合性,同時具備了回調渲染沒有的靈活性。
Hook
在解決上面demo的例子,hook只是換了一種代碼展示形式,解決方法是沒變的,我們看改完的栗子
import ContainerComponent from "./ContainerComponent"; import ShowComponent from "./ShowComponent"; { ReactDOM.render( <ContainerComponent render={({ count, handlePlus }) => ( <ShowComponent count={count} handlePlus={handlePlus} /> )} />, document.getElementById("app") ); }
ContainerComponent
import React, { useState, useEffect } from "react";
import ShowComponent from './ShowComponent';
function ContainerComponent(props) {
const [count, setCount] = useState(0);
const handlePlus = () => {
setCount(count + 1);
}
return <div>
<h1>{`I am a container component.`}</h1>
{
props.render({
count: count,
handlePlus: handlePlus
})
}
</div>
}
export default ContainerComponent;
ShowComponent
import React, { useState, useEffect } from "react";
function ShowComponent(props) {
return <div>
<p>{`當前計數:${props.count}`}</p>
<button onClick={props.handlePlus}>plus</button>
</div>
}
export default ShowComponent;
依然利用了RenderProps,只是將Class組件變成了Hook組件,這個其實體現不出來Hook的要解決的問題,
Hook解決的問題跟HOC是比較類似的,側重點是邏輯復用。看個栗子。
公用部分
function useFetch() { const [data, setData] = useState(); useEffect(() => { new Promise((resolve) => { resolve({ name: "dqhan", age: 28, }); }).then((res) => { setData(res); }); }, []); return data; }
組件App
function App() { let data = useFetch(); return ( <div> <div>App1</div> </div> ); }
組件App2
function App2() { let data = useFetch(); return ( <div> <div>App2</div> </div> ); }
提取組件加載完成時獲取數據的復用邏輯,這樣能讓代碼復用看起來更直觀。當然還有最后一種方式實現代碼復用,就是HOC。這里就不談了。
