首先來看下vue的slot的實現
<base-layout>組件,具名插槽name定義以及默認插槽
<div class="container"> <header> <slot name="header"></slot> </header> <main> <slot></slot> </main> <footer> <slot name="footer"></slot> </footer> </div> <template>
提供內容渲染的組件
<base-layout> <template v-slot:header> <h2>Here might be a page title</h2> </template> <p>A paragraph for the main content.</p> <p>And another one.</p> <template v-slot:footer> <p>Here's some contact info</p> </template> </base-layout>
最終會渲染出已下架結構
<base-layout> <template v-slot:header> <h2>Here might be a page title</h2> </template> <p>A paragraph for the main content.</p> <p>And another one.</p> <template v-slot:footer> <p>Here's some contact info</p> </template> </base-layout>
言歸正傳,怎樣使用react的context實現vue的這一功能呢
1 首先確認下layout組件的結構
import React, { Component } from 'react';
import SlotProvider from './SlotProvider'
import Slot from './Slot'
class AppLayout extends Component {
static displayName = 'AppLayout'
render () {
return (
<div className="container">
<header>
<Slot name="header"></Slot>
</header>
<main>
<Slot></Slot>
</main>
<footer>
<Slot name="footer"></Slot>
</footer>
</div>
)
}
}
export default SlotProvider(AppLayout)
2 對此結構輸出具體內容的組件
import React, { Component } from 'react';
import AppLayout from './AppLayout'
import AddOn from './AddOn'
export default class App extends Component {
render() {
return (
<AppLayout>
<AddOn slot="header">
<h2>這里可能是一個頁面標題</h2>
</AddOn>
<AddOn>
<p>主要內容的一個段落。</p>
<p>另一個段落。</p>
</AddOn>
<AddOn slot="footer">
<p>這里有一些聯系信息</p>
</AddOn>
</AppLayout>
)
}
}
3 其中AddOn類似于上面vue的template,所以下面來實現這個簡單的只是用來提供slot標識和children內容的組件AddOn的實現
import React from 'react';
import PropTypes from 'prop-types'
const AddOn = () => null
AddOn.propTypes = { slot: PropTypes.string }
AddOn.defaultTypes = { slot: '$$default' }
AddOn.displayName = 'AddOn'
export default AddOn
4 Slot組件
import React from 'react';
import { SlotContext } from './SlotProvider'
import PropTypes from 'prop-types'
const Slot = ({ name, children }) => {
return (
<SlotContext.Consumer>
{(value) => {
const addOnRenderer = value.requestAddOnRenderer(name)
return (addOnRenderer && addOnRenderer()) || children || null
}}
</SlotContext.Consumer>
)
}
Slot.displayName = 'Slot'
Slot.propTypes = { name: PropTypes.string }
Slot.defaultProps = { name: '$$default' }
export default Slot
5 接下來就是對Slot進行解析的HOC SlotProvider組件了
import React, { Component } from 'react';
function getDisplayName(component) {
return component.displayName || component.name || 'component'
}
export const SlotContext = React.createContext({
requestAddOnRenderer: () => { }
})
const SlotProviderHoC = (WrappedComponent) => {
return class extends Component {
static displayName = `SlotProvider(${getDisplayName(WrappedComponent)})`
// 用于緩存每個<AddOn />的內容
addOnRenderers = {}
requestAddOnRenderer = (name) => {
if (!this.addOnRenderers[name]) {
return undefined
}
return () => (
this.addOnRenderers[name]
)
}
render() {
const {
children,
...restProps
} = this.props
if (children) {
// 以k-v的方式緩存<AddOn />的內容
const arr = React.Children.toArray(children)
const nameChecked = []
this.addOnRenderers = {}
arr.forEach(item => {
const itemType = item.type
console.log('itemType',itemType)
if (item.type.displayName === 'AddOn') {
const slotName = item.props.slot || '$$default'
// 確保內容唯一性
if (nameChecked.findIndex(item => item === slotName) !== -1) {
throw new Error(`Slot(${slotName}) has been occupied`)
}
this.addOnRenderers[slotName] = item.props.children
nameChecked.push(slotName)
}
})
}
return (
<SlotContext.Provider value={{ requestAddOnRenderer: this.requestAddOnRenderer }}>
<WrappedComponent {...restProps} />
</SlotContext.Provider >
)
}
}
}
export default SlotProviderHoC
6 最終渲染結果

總結
以上所述是小編給大家介紹的使用react context 實現vue插槽slot功能,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對億速云網站的支持!
如果你覺得本文對你有幫助,歡迎轉載,煩請注明出處,謝謝!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。