- 组件复合 - Composition,是另一种区别于高阶组件的一种对扩展组件的方式,可以理解为Vue中的slot
- 下面我们有一个
Diaolog
组件,作为容器,只规定了外层样式,具体的插入的内容由插入的组件决定
// src/components/Composition.js
import React from 'react'
// Dialog作为容器不关心内容和逻辑,只负责提供外层样式
// 插入的内容{props.children}可理解为vue中的slot
function Dialog (props) {
return (
<div style={{ border: "4px solid red"}}>
{/* children是固定写法 */}
{props.children}
</div>
)
}
// WelcomeDialog组件通过复合提供内容
function WelcomeDialog (props) {
return (
<Dialog>
<h1>welcome</h1>
<p>感谢使用</p>
</Dialog>
)
}
export default function () {
return (
<div>
<WelcomeDialog></WelcomeDialog>
</div>
)
}
复合组件WelcomeDialog.jpg
// src/components/Composition.js
import React from 'react'
function Dialog (props) {
return (
// 边框默认颜色为red,如果有配置则获取配置颜色
<div style={{ border: `4px solid ${props.color || 'red'}` }}>
{/* children是固定写法 */}
{props.children}
</div>
)
}
function WelcomeDialog (props) {
return (
<Dialog {...props}>
<h1>welcome</h1>
<p>感谢使用</p>
</Dialog>
)
}
export default function () {
return (
<div>
<WelcomeDialog color={'blue'}></WelcomeDialog>
</div>
)
}
- 复合组件也可以像Vue中一样使用具名插槽,在组件中插入的这部分内容对应显示在容器组件同名的位置
// src/components/Composition.js
import React from 'react'
// 这里可以看出props.children、props.footer都可以是JSX
// props.children可以写成任意的Js表达式
function Dialog (props) {
return (
// 边框默认颜色为red,如果有配置则获取配置颜色
<div style={{ border: `4px solid ${props.color || 'red'}` }}>
{/* children是固定写法 */}
{props.children}
{/* 具名插槽 footer */}
<div className="footer">
{props.footer}
</div>
</div>
)
}
export default function () {
let footer = <button onClick={() => {alert('confirm')}}>this is footer</button>
return (
<div>
<WelcomeDialog color={'blue'} footer={footer}></WelcomeDialog>
</div>
)
}
- 前面说了
props.chilrdren
可以是任意Js表达式,所以 props.chilrdren
肯定可以是一个函数,然后我们用函数的形式模拟Vue中的作用域插槽
// src/components/Composition.js
import React from 'react'
// 模拟这里是异步调用
const Api = {
getUser() {
return {name: 'asher', age: 3}
}
}
function Fetcher (props) {
// 通过组件传进来的属性调用api获取数据
let user = Api[props.name]();
// 这里的props.children是个函数
return props.children(user)
}
export default function () {
return (
<div>
<Fetcher name="getUser">
// 因为props.children是个函数,所以在这里直接调用
{({name, age}) => (
<p>{name} - {age}</p>
)}
</Fetcher>
</div>
)
}
- 再试一试
props.children
是一个数组的时候,做一个简单的过滤器
// src/components/Composition.js
import React from 'react'
function Filter (props) {
return (
<div>
{React.Children.map(props.children, child => {
if(child.type !== props.type) {
return;
}
// return过滤之后的数组
return child;
})}
</div>
)
}
export default function () {
return (
<div>
{/* 过滤器 过滤出指定标签*/}
<Filter type="h5">
<h5>re</h5>
<h5>eae</h5>
<p>this is p</p>
</Filter>
</div>
)
}
- 复合组件中还可以修改
children
,这里我们做一个单选按钮组件,我们都知道同组单选按钮要想实现单选必须拥有相同的 name
属性,这里我们需要实现这个 name
属性由父组件 RadioGroup
决定并传递给每一个子组件 Radio
// src/components/Composition.js
import React from 'react'
// 修改children
function RadioGroup (props) {
return (
<div>
{/* RadioGroup遍历自己的children,并分别给他们加上相同的name属性 */}
{React.Children.map(props.children, child => {
// vdom不可更改,必须先clone一个再做更改
// 不可以写成 child.props.name = props.name
return React.cloneElement(child, {name:props.name})
})}
</div>
)
}
function Radio ({children, ...rest}) {
// 因为Radio组件中还有内容,所以要特别处理children
// 将传进来的props分成2部分,分别是children和其他属性
return (
<label>
<input type="radio" {...rest}></input>
{children}
</label>
)
}
export default function () {
return (
<div>
<RadioGroup name="mvvm">
<Radio value="vue">vue</Radio>
<Radio value="react">react</Radio>
<Radio value="angular">angular</Radio>
</RadioGroup>
</div>
)
}
网友评论