# 一、props 属性
# 1、props 传值
react 中父向子传递数据和 vue 类似,我们可以通过
在父组件中的子组件标签上添加 自定义属性
然后子组件通过 constructor(props)获取数据
- 父组件
class App extends React.Component {
constructor() {
this.state = {
user: 'lencamo'
// info: {
// //
// }
}
}
//
render() {
const { user } = this.state
return <HelloWorld userName={user} />
// 批量传值
// return <HelloWorld {...this.state.info} />
}
}
- 子组件
class HelloWorld extends React.Component {
// 默认可以省略
// constructor(props) {
// super(props)
// }
//
render() {
const { userName } = this.props
return <h2>{userName}</h2>
}
}
当然,在 vue 中 props 选项的数据是可以进行类型验证的,并还支持数组和对象两种方式
在 react 项目中,如果使用了 Flow 或者 Typescript 那么可以直接进行类型验证
如果没有的话,可以使用 prop-types (opens new window) 库来进行参数验证,具体使用可以参考 使用 PropTypes 进行类型检查 (opens new window)
props 类型检测
import PropTypes from 'prop-types'
class HelloWorld extends React.Component {
render() {
const { userName } = this.props
return <h2>{userName}</h2>
}
}
// props 类型验证
HelloWorld.propTypes = {
userName: PropTypes.string.isRequired,
barList: PropTypes.array
}
// props 默认值
HelloWorld.defaultProps = {
userName: '用户123'
}
# 2、props 回调
在 vue 中,我提到过,使用 v-on 和使用 props 的区别。
一个通过 props 传递回调函数供子组件调用
另外一个是通过语法糖 v-on 为子组件 vc 身上绑定自定义事件供子组件通过$emit 触发。
在 react 中没有过多的语法糖,所以采用的是 props 的方式:
- 父组件
class App extends React.Component {
constructor() {
this.state = {
count: 1
}
}
// 创建回调
countChange(n) {
this.setState({
count: this.state.counter + n
})
}
render() {
const { count } = this.state
return (
<>
<h2>当前计数:{count} </h2>
<HelloWorld countChange={(n) => this.countChange(n)} />
</>
)
}
}
- 子组件
class HelloWorld extends React.Component {
// 触发回调
addClick(n) {
this.props.countChange(n)
}
render() {
return (
<div>
<button onClick={() => addClick(5)}>+5</button>
<button onClick={() => subClick(-3)}>-3</button>
</div>
)
}
}
# 二、react 插槽
react 中其实是没有插槽的,只不过是我们想在 react 实现类似于 vue 中的插槽功能。
优点:开发者自己能实现的的简单功能,就不要干涉,让开发者自由发挥
# 1、props 与 children
const element = createElement(type, props, ...children)
前面我们使用子组件时,采用的都单标签。实际上,我们使用可以使用双标签的,并且标签内的元素可以在 this.props.children 中找到。
每个组件都可以获取到 props.children。它包含组件的开始标签和结束标签之间的内容。
- 父组件
children 与 单个子元素 ✍
当传递单个子元素时,children 并不是一个数组,而是子元素本身
具体可以查看:react 源码实现 (opens new window)
NavBar.propTypes = {
children: PropTypes.element
}
class App extends React.Component {
//
render() {
return (
<div>
<NavBar>
<button>左箭头</button>
<input placeholder="请输入" />
<button>搜索</button>
</NavBar>
</div>
)
}
}
- 子组件
class NavBar extends React.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>
)
}
}
# 2、props 与 DOM
当面的第一种方法,已经体验完了,但是要注意只传递一个元素的情况。
下面我们看看,另外一种: 直接通过 props 向子组件传递元素
- 父组件
class App extends React.Component {
//
render() {
return (
<div>
<NavBar
letfSlot={<button>左箭头</button>}
centerSlot={<input placeholder="请输入" />}
rightSlot={<button>搜索</button>}
/>
</div>
)
}
}
- 子组件
class NavBar extends React.Component {
render() {
const { letfSlot, centerSlot, rightSlot } = this.props
return (
<div className="nav-bar">
<div className="left">{letfSlot}</div>
<div className="center">{centerSlot}</div>
<div className="right">{rightSlot}</div>
</div>
)
}
}
# 3、作用域插槽 *
简单一句话:使用回调函数(向子组件传递元素,并获取子组件数据)
- 父组件
class App extends React.Component {
constructor() {
//
this.state = {
titles: [
{ id: 1, title: '' },
{ id: 2, title: '' },
{ id: 3, title: '' },
{ id: 4, title: '' }
]
}
}
//
render() {
return (
<div>
{/* 使用回调获取子组件数据 */}
<ItemList title="titles" itemSlot={(msg) => <p>{msg}</p>} />
</div>
)
}
}
- 子组件
class ItemList extends React.Component {
constructor() {
//
this.state = {
msgs: ['', '', '', '']
}
}
render() {
const { titles, itemSlot } = this.props
const { msg } = this.state
return (
<div>
{msgs.map((item, index) => {
return (
<>
<h2>{titles[index].title || '未找到匹配的标题'}</h2>
{/* 使用父组件数据的同时,触发回调并向其传递数据 */}
<div className="item-list">{itemSlot(item)}</div>
</>
)
})}
</div>
)
}
}
# 三、Context
在 react 中的跨组件通信方案有:EventBus、Context、Redux 等,下面先介绍 Context 就可以了,
提示
真实开发中,我们可以采用:
- 方案 1:Context + props
- 方案 2:react-Redux / react-toolkit
Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。
对比 vue 的依赖注入
vue 中的依赖注入采用的是:(Provide、Inject)
<script>
export default {
// provide 提供给后代✨组件的数据/方法
// provide: {
// message: 'provided by father'
// }
provide() {
return {
message: 'provided by father'
}
}
}
</script>
<script>
export default {
// 使用 inject 选项来接收由provide提供的、当前组件✨需要的数据/方法
inject: ['message']
}
</script>
对比 crateRef() 使用
createRef()是在同一组件下的用于获取 Dom 的方法
class Demo extends React.Component {
constructor() {
// 绑定到ref对象上
this.bookRef = React.createRef()
}
getNativeDom() {
console.log(this.bookRef.current)
}
render() {
return (
<div>
<p ref={this.bookRef}>JavaScript高级程序设计</p>
<p>你不知道的js</p>
<button onClick={(e) => this.getNativeDom()}>获取DOM</button>
</div>
)
}
}
下面的这个 Context 的使用方式有点类似于 依赖注入和 crateRef()的结合体:
const xxxContext = createContext()
搭配 1
<xxxContext.Provider value={ data }></xxxContext.Provider>
<xxxContext.Consumer>{(value) => { // }}</xxxContext.Consumer>
搭配 2
<xxxContext.Provider value={ data }></xxxContext.Provider>
componentName.contextType = xxxContext
+this.context
# 1、创建 context
- context/index.js
import { createContext } from 'react'
const ThemeContext = createContext()
// createContext('默认值')
const UserContext = createContext('普通用户')
export { ThemeContext, UserContext }
# 2、发送数据方
import { ThemeContext } from './context/index.js'
class App extends React.Component {
//
render() {
return (
<ThemContext.Provider value={{ color: 'red', size: '33' }}>
<HelloWorld />
</ThemContext.Provider>
)
}
}
# 3、接收数据方
- 方式 1:使用 contextType + this.context
import { ThemeContext } from './context/index.js'
// 方式1
class Soners extends React.Component {
// static contextType = ThemeContext
render() {
const theme = this.context
return <Button style={{ ...theme }} />
}
}
// 指定 contextType 读取当前的 theme context
Soners.contextType = ThemeContext
- 方式 2:使用
<ThemeContext.Consumer>
+ value
import { ThemeContext } from './context/index.js'
// 方式2✨(同样适用于函数式组件)
class Soners extends React.Component {
//
render() {
return (
<ThemeContext.Consumer>
{(value) => {
return <Button style={{ ...value }} />
}}
</ThemeContext.Consumer>
)
}
}
← react组件(上) react组件(下) →