前言

在开发中,我们抽取了一个组件,但是为了让这个组件具备更强的通用性,我们不能将组件中的内容限制为固定的div、span等等这些元素。我们应该让使用者可以决定某一块区域到底存放什么内容。

这种需求在Vue当中有一个固定的做法是通过slot来完成的,React呢?

React对于这种需要插槽的情况非常灵活,有两种方案可以实现:

  • 组件的children子元素;
  • props属性传递React元素;

children实现插槽

每个组件都可以获取到 props.children:它包含组件的开始标签和结束标签之间的内容。

注意

  • 如果组件组件的开始标签和结束标签之间的内容有多个,此时通过this.props.children获取到的是一个数组;
  • 如果组件组件的开始标签和结束标签之间的内容只有一个,此时通过this.props.children获取到的就是内容本身;

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React, { Component } from 'react'
import './style.css' // 注意,这种方式写的样式,是全局的样式,会造成冲突

export default class NavBar extends Component {
render() {
const { children } = this.props
return (
<div className="nav-bar">
<div className="left">{children[0]}</div>
<div className="center">{children[1]}</div>
<div className="right">{children[2]}</div>
</div>
)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React, { Component } from 'react'
import NavBar from './NavBar'
export default class App extends Component {
render() {
return (
<div>
<NavBar>
<button>我是按钮</button>
<h2>我是标题</h2>
<span>span</span>
</NavBar>
</div>
)
}
}

最终效果:

image-20230502173349472

拓展

问题描述:我只想让别人传入一个元素,该如何限制?

解决方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import './style.css'

class NavBar extends Component {
render() {
const { children } = this.props
return (
<div className="nav-bar">
<div className="left">{children[0]}</div>
<div className="center">{children[1]}</div>
<div className="right">{children[2]}</div>
</div>
)
}
}

NavBar.propTypes = {
children: PropTypes.element, // 限制一个
children: PropTypes.array, // 限制多个
}

export default NavBar

弊端

通过children这种方法,需要记住传入元素的索引值,否则会出现意料之外的错误

使用props实现插槽

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React, { Component } from 'react'

class NavBar extends Component {
render() {
const { leftSlot, centerSlot, rightSlot } = this.props
return (
<div className="nav-bar">
<div className="left">{leftSlot}</div>
<div className="center">{centerSlot}</div>
<div className="right">{rightSlot}</div>
</div>
)
}
}
export default NavBar
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import React, { Component } from 'react'
import NavBar2 from './NavBar2'
export default class App extends Component {
render() {
const btn = <button>我是按钮2</button>
return (
<div>
{/* 通过props的方式实现插槽 */}
<NavBar2
leftSlot={btn}
centerSlot={<h2>我是标题2</h2>}
rightSlot={<span>span2</span>}
/>
</div>
)
}
}