使用react context 实现vue插槽slot功能

2020-06-13 10:46:32易采站长站整理

}

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 }}>