React - render props和高階組件
1怀偷,render props模式
使用步驟
- 1家厌,創(chuàng)建一個(gè)組件,在組件中提供復(fù)用的狀態(tài)邏輯代碼
- 2椎工,將要復(fù)用的狀態(tài)作為props.render(state)方法的參數(shù)饭于,暴露到組件外部
- 3,使用props.render()的返回值作為要渲染的內(nèi)容
class 組件名 extends React.Component {
state = {}
render() {
return this.props.render(this.state)
}
}
<組件名 render={i => {
return (
<div>{i.xx}</div>
)
}}/>
示例:
class Mouse extends React.Component {
state = {
x: 0,
y: 0
}
handleMouseMove = e => {
this.setState({
x: e.clientX,
y: e.clientY
})
}
// 監(jiān)聽鼠標(biāo)移動(dòng)事件
componentDidMount() {
window.addEventListener('mousemove', this.handleMouseMove)
}
render() {
return this.props.render(this.state)
}
}
class App extends React.Component {
render() {
return (
<div>
<h1>哈哈哈哈</h1>
<Mouse render={i => {
return (
<div>鼠標(biāo)位置:{i.x}, {i.y}</div>
)
}}/>
<Mouse render={i => {
return (
<img src={img} style={{position: 'absolute', top: i.y - 100, left: i.x - 100}}></img>
)
}}/>
</div>
)
}
}
children代替render屬性
- 并不是該模式叫render props就必須使用名為render的prop维蒙,實(shí)際上可以使用任意名稱的prop
- 把prop是一個(gè)函數(shù)并且告訴組件要渲染什么內(nèi)容的技術(shù)叫做:render props模式
- 推薦使用children代替render屬性
class Mouse extends React.Component {
state = {
x: 0,
y: 0
}
handleMouseMove = e => {
this.setState({
x: e.clientX,
y: e.clientY
})
}
// 監(jiān)聽鼠標(biāo)移動(dòng)事件
componentDidMount() {
window.addEventListener('mousemove', this.handleMouseMove)
}
// 代碼優(yōu)化項(xiàng) 2
// 在卸載的時(shí)候解除mousemove事件綁定
componentWillUnmount() {
window.removeEventListener('mousemove', this.handleMouseMove)
}
render() {
// return this.props.render(this.state)
// children代替render
return this.props.children(this.state)
}
}
// 代碼優(yōu)化項(xiàng) 1
Mouse.prototypes = {
children: PropTypes.func.isRequired,
}
class App extends React.Component {
render() {
return (
<div>
<h1>哈哈哈哈</h1>
{/* <Mouse render={i => {
return (
<div>鼠標(biāo)位置:{i.x}, {i.y}</div>
)
}}/> */}
{/* children代替render */}
<Mouse>
{
({x, y}) => {
return (
<div>鼠標(biāo)位置:{x}, {y}</div>
)
}
}
</Mouse>
{/* <Mouse render={i => {
return (
<img src={img} style={{position: 'absolute', top: i.y - 100, left: i.x - 100}}></img>
)
}}/> */}
{/* children代替render */}
<Mouse>
{
({x, y}) => {
return (
<img src={img} style={{position: 'absolute', top: y - 100, left: x - 100}}></img>
)
}
}
</Mouse>
</div>
)
}
}
2, 高階組件(HOC)
目的是實(shí)現(xiàn)狀態(tài)邏輯復(fù)用镰绎。
使用步驟:
- 1,創(chuàng)建一個(gè)函數(shù)木西,名稱約定以with開頭
- 2,指定函數(shù)參數(shù)随静,參數(shù)應(yīng)該以大寫字母開頭(作為要渲染的組件)
- 3八千,在函數(shù)內(nèi)部創(chuàng)建一個(gè)類組件,提供復(fù)用的狀態(tài)邏輯代碼燎猛,并返回
- 4恋捆,在該組件中,渲染參數(shù)組件重绷,同時(shí)將狀態(tài)通過(guò)prop傳遞給參數(shù)組件
- 5沸停,調(diào)用該高階組件,傳入要增強(qiáng)的組件昭卓,通過(guò)返回值拿到增強(qiáng)后的組件愤钾,并將其渲染到頁(yè)面中
function withMouse(WrappendComponent) {
class Mouse extends React.Component{
return Mouse
}
}
// Mouse組件的render方法中:
return <WrappendComponent {...this.state}/>
// 創(chuàng)建組件
const MousePosition = widthMouse(Position)
// 渲染組件
<MousePosition />
以上示例改為高階組件:
// 創(chuàng)建高階組件
function widthMouse(WrappedComponent) {
// 該組件提供復(fù)用的狀態(tài)邏輯
class Mouse extends React.Component {
state = {
x: 0,
y: 0
}
handleMouseMove = e => {
this.setState({
x: e.clientX,
y: e.clientY
})
}
componentDidMount() {
window.addEventListener('mousemove', this.handleMouseMove)
}
componentWillUnmount() {
window.removeEventListener('mousemove', this.handleMouseMove)
}
render() {
return <WrappedComponent {...this.state}/>
}
}
return Mouse
}
// 用來(lái)測(cè)試高階組件 獲取鼠標(biāo)移動(dòng)的位置的組件
const Position = props => (
<p>
鼠標(biāo)當(dāng)前位置: (x: {props.x}, y: {props.y})
</p>
)
// 用來(lái)測(cè)試獲取圖片跟隨鼠標(biāo)動(dòng)的組件
const ImgPosition = props => (
<img src={img} style={{
position: 'absolute',
top: props.y - 100,
left: props.x - 100
}}></img>
)
// 獲取增強(qiáng)后的組件
const MousePosition = widthMouse(Position)
const MouseImg = widthMouse(ImgPosition)
class App extends React.Component {
render() {
return (
<div>
<h1>高階組件</h1>
<MousePosition/>
<MouseImg/>
</div>
)
}
}
設(shè)置displayName
- 使用高階組件存在的問(wèn)題:得到的兩個(gè)組件名稱相同
- 原因:默認(rèn)情況下,React使用組件名稱作為displayName
- 解決方式: 為高階組件設(shè)置displayName便于調(diào)試的時(shí)候區(qū)分不同的組件
- displayName的作用:用于設(shè)置調(diào)試信息(React developer tools)
- 設(shè)置方式
Mouse.displayName = `widthMouse${getDisplayName(WrappedComponent)}`
function getDisplayName(WrappedComponent) {
return WrappedComponent.displayName || WrappedComponent.name || 'xiaowang'
}
以上示例改寫為設(shè)置displayName
function widthMouse(WrappedComponent) {
// 該組件提供復(fù)用的狀態(tài)邏輯
class Mouse extends React.Component {
state = {
x: 0,
y: 0
}
handleMouseMove = e => {
this.setState({
x: e.clientX,
y: e.clientY
})
}
componentDidMount() {
window.addEventListener('mousemove', this.handleMouseMove)
}
componentWillUnmount() {
window.removeEventListener('mousemove', this.handleMouseMove)
}
render() {
return <WrappedComponent {...this.state}/>
}
}
// 設(shè)置displayName
Mouse.displayName = `widthMouse${getDisplayName(WrappedComponent)}` // widthMousePosition widthMouseImgPosition
return Mouse
}
function getDisplayName(WrappedComponent) {
console.log('WrappedComponent=====>', WrappedComponent.displayName) // undefined undefined
console.log('WrappedComponent=====>', WrappedComponent.name) // Position ImgPosition
return WrappedComponent.displayName || WrappedComponent.name || 'xiaoWangComponent'
}
// 用來(lái)測(cè)試高階組件 獲取鼠標(biāo)移動(dòng)的位置的組件
const Position = props => (
<p>
鼠標(biāo)當(dāng)前位置: (x: {props.x}, y: {props.y})
</p>
)
// 用來(lái)測(cè)試獲取圖片跟隨鼠標(biāo)動(dòng)的組件
const ImgPosition = props => (
<img src={img} style={{
position: 'absolute',
top: props.y - 100,
left: props.x - 100
}}></img>
)
// 獲取增強(qiáng)后的組件
const MousePosition = widthMouse(Position)
const MouseImg = widthMouse(ImgPosition)
class App extends React.Component {
render() {
return (
<div>
<h1>高階組件</h1>
<MousePosition/>
<MouseImg/>
</div>
)
}
}
高階組件 傳遞props
- props丟失
- 原因:高階組件沒(méi)有往下傳遞props
- 解決方式:渲染W(wǎng)rappedComponent時(shí)候醒,將state和this.props一起傳遞給組件
- 傳遞方式
<WrappedComponent {...this.state} {...this.props}></WrappedComponent>
以上示例涉及到修改的代碼:
function widthMouse(WrappedComponent) {
// 該組件提供復(fù)用的狀態(tài)邏輯
// class Mouse extends React.Component {
// state = {
// x: 0,
// y: 0
// }
// handleMouseMove = e => {
// this.setState({
// x: e.clientX,
// y: e.clientY
// })
// }
// componentDidMount() {
// window.addEventListener('mousemove', this.handleMouseMove)
// }
// componentWillUnmount() {
// window.removeEventListener('mousemove', this.handleMouseMove)
// }
render() {
// Mouse組件中可以拿到other這個(gè)屬性
console.log('this.props=====>', this.props)
return <WrappedComponent {...this.state} {...this.props}/>
}
}
// 設(shè)置displayName
// Mouse.displayName = `widthMouse${getDisplayName(WrappedComponent)}` // widthMousePosition widthMouseImgPosition
return Mouse
}
// function getDisplayName(WrappedComponent) {
// console.log('WrappedComponent=====>', WrappedComponent.displayName) // undefined undefined
// console.log('WrappedComponent=====>', WrappedComponent.name) // Position ImgPosition
// return WrappedComponent.displayName || WrappedComponent.name || 'xiaoWangComponent'
// }
// 用來(lái)測(cè)試高階組件 獲取鼠標(biāo)移動(dòng)的位置的組件
const Position = props => {
// position組件沒(méi)有拿到other屬性
console.log('position組件:=====>', props)
return (
<p>
鼠標(biāo)當(dāng)前位置: (x: {props.x}, y: {props.y})
</p>
)
}
// 用來(lái)測(cè)試獲取圖片跟隨鼠標(biāo)動(dòng)的組件
// const ImgPosition = props => (
// <img src={img} style={{
// position: 'absolute',
// top: props.y - 100,
// left: props.x - 100
// }}></img>
// )
// 獲取增強(qiáng)后的組件
// const MousePosition = widthMouse(Position)
// const MouseImg = widthMouse(ImgPosition)
class App extends React.Component {
render() {
return (
<div>
{/* <h1>高階組件</h1> */}
<MousePosition other="哈哈哈"/>
{/* <MouseImg/> */}
</div>
)
}
}