组件通讯
- 组件是独立封闭的单元
- 一个完整的页面通常是由多个组件组合而成的
- 组件之间经常需要共享一些数据,所以就需要组件之间能够通讯
props
props
用于接收外界向组件中传递的数据
实现方法:
- 渲染组件时,在组件标签中使用自定义属性向组件内部传递数据
- 组件内部可以使用 props 来接收自定义属性传递的数据
- 函数组件: props
- 类组件: this.props
注意事项:
- 除了字符串之外,其他类型的数据都需要使用 {}
- props 是
只读属性
,不能修改里面的数据 - 使用类组件时,如果写了构造函数,则需要将 props 传递给 super(),否则在 constructor 中无法使用props (其他方法可以使用)
类组件中接收自定义属性中的数据
类组件方式1:
import React from 'react'
import ReactDOM from 'react-dom'
class App extends React.Component{
render () {
return (
<div>
大家好,我叫{
this.props.name},今年{
this.props.age}岁
</div>
)
}
}
ReactDOM.render(<App name='zs' age={
18} />,document.querySelector('#app'))
组件内部会议对象形式将数据保存在props
属性中
this.props = {
name:'zs',
age: 18
}
调用组件时,使用自定义属性将数据传递到组件内部
this.props.name
this,props.age
类组件方式2:
如果写了构造函数,则需要将props传递给super()
,否则在constructor
中无法使用props(其他方法可以使用)
import React from 'react'
import ReactDOM from 'react-dom'
class App extends React.Component{
constructor (props) {
super(props)
//console.log(this.props)
}
render () {
return (
<div>
大家好,我叫{
this.props.name},今年{
this.props.age}岁
</div>
)
}
}
ReactDOM.render(<App name='zs' age={
18} />,document.querySelector('#app'))
函数组件中接收自定义属性中的数据
import React from 'react'
import ReactDOM from 'react-dom'
const App = (props) => {
reyurn (
<div>
大家好,我叫{
props.name},今年{
props.age}岁
</div>
)
}
ReactDOM.render(<App name='zs' age={
18} />,document.querySelector('#app'))
父子组件嵌套
在一个组件中可以嵌套另一个组件,外层的组件就是父组件,内层的组件就是子组件
实现方法: 父组件的== render 方法==中调用子组件
import React from 'react';
import ReactDOM from 'react-dom';
import './assets/css/fs.css'
// 创建子组件
class Son extends React.Component {
render () {
return (
<div className="son">我是子组件</div>
)
}
}
// 创建父组件
class Father extends React.Component {
render () {
return (
<div className="father">
<div>我是父组件</div>
{
/* 父组件在render方法中渲染子组件 */}
<Son />
</div>
)
}
}
ReactDOM.render(<Father />, document.querySelector('#app'))
父向子传值
父组件渲染子组件时,将state中的数据添加到子组件的自定义属性中
实现要点:
- 父组件要有state,内部保存准备传递给子组件的数据
- 父组件调用子组件时,为子组件添加自定义属性,将 state 中的数据设置为属性的值
- 子组件中使用props接收父组件数据
import React from 'react'
import ReactDOM from 'react-dom'
import './assets/css/fs.css'
// 目标: 子组件接收到父组件的数据
class Son extends React.Component {
render () {
return (
<div className="son">
我是子组件<br />
{
/* 子组件使用 props 来接收父组件数据 */}
接收到父组件的数据为: {
this.props.name} - {
this.props.age}
</div>
)
}
}
class Father extends React.Component {
state = {
name: '二狗子',
age: 8
}
render () {
return (
<div className="father">
我是父组件
{
/* 父组件在调用子组件时,将数据通过自定义属性传递给子组件 */}
<Son name={
this.state.name} age={
this.state.age} />
</div>
)
}
}
ReactDOM.render(<Father />, document.querySelector('#app'))
子向父传值
核心: 利用回调函数
- 父组件
- 父组件中创建一个接收子组件数据的函数
- 父组件调用子组件时,将函数作为参数传给子组件
- 子组件 (子组件已经将父组件函数保存在 props 中)
- 子组件中使用 state 定义要传递给父组件的数据
- 子组件利用事件调用父组件传递的函数,并将数组作为参数传入
import React from 'react'
import ReactDOM from 'react-dom'
import './assets/css/fs.css'
// 目标: 将子组件中的数据传递到父组件中
class Son extends React.Component {
state = {
name: '翠花',
age: 18
}
handleClick = () =>{
console.log('click')
console.log(this.props.name)
this.props.fn(this.state)
}
render () {
return (
<div className="sdiv">
<div>我是子组件</div>
<button onClick={
this.handleClick}>发送</button>
</div>
)
}
}
class Father extends React.Component {
getChildData = (data) =>{
console.log("getChildData:",data)
}
render () {
return (
<div className="fdiv">
<div>我是父组件</div>
<Son name="zs" age={
18} fn={
this.getChildData}/>
</div>
)
}
}
ReactDOM.render(<Father />, document.querySelector('#app'))
兄弟组件传值
思路: 子向父传值 、 父向子传值 两步的结合
- 子组件B 通过调用父组件的函数将数据传递给父组件 (子向父传值)
- 父组件再通过子组件属性向子组件A传值 (父向子传值)
核心名词: 状态提升
状态提升:将数据统一放在父组件中进行保存,父组件既要提供state保存数据,也要提供方法,让子组件能向父组件进行传值
Context解决的问题:
Context
- 传统方式缺陷明显,一层一层传递太过复杂
- 解决方案: 使用 Context 可以实现跨组件传值
- 实现步骤:
- 调用 React.createContext() 方法创建 Provider (提供数据) 和 Consumer (消费数据)
- 使用 Provider 组件包裹最外层的父组件,并使用 value属性传递数据
- 使用 Consumer 接收数据
import React from 'react'
import ReactDOM from 'react-dom'
import './css/context.css'
//1. 调用 React.createContext() 方法创建 Provider 和 Consumer 两个组件
const {
Provider, Consumer} = React.createContext()
class App extends React.Component {
state = {
msg: '我是 App.state.msg 的数组'
}
render () {
return (
//2. 使用 Provider 组件包裹整个组件, 使用 value 属性传递数据
<Provider value={
this.state.msg}>
<div className="app">
App组件
<Child />
</div>
</Provider>
)
}
}
const Child = () => {
return (
<div className="child">
Child组件
<SubChild />
</div>
)
}
const SubChild = () => {
return (
<div className="subchild">SubChild组件:
{
/* 3. 使用 Consumer 组件来接收 Provider 提供的数据 */}
<Consumer>
{
data => <span>{
data}</span>}
</Consumer>
</div>
)
}
ReactDOM.render(<App />, document.querySelector('#app'))
Consumer 中是一个函数,参数data中就保存了Provider中value提供的数据
props其他功能
children 属性
- render 方法在渲染组件标签时可以使用双标签
- 组件使用双标签时,内部可以填入子节点,例如:文本子节点、标签子节点、JSX子节点、函数子节点
- props.children 中以数组形式保存这些子节点
import React from 'react'
import ReactDOM from 'react-dom'
class Show extends React.Component {
render () {
return (
<div>我是show</div>
)
}
}
class App extends React.Component {
render () {
// App组件子节点的信息都保存在 this.props.children 属性中
console.log(this.props)
return (
<div>我是App组件</div>
)
}
}
ReactDOM.render(
<App>
<h1>我是写在App中的内容</h1>
<Show>哈哈哈</Show>
<div>
{
() => {
console.log('兄弟12345')
}
}
</div>
</App>,
document.querySelector('#app')
)
props校验
- props用来接收外来数据,这些数据可以在组件中被使用
- 但是组件的调用者可能会传入一些不符合规则的数据
- 当组件调用者传入不符合规则的数据时,用该进行错误提示
props校验的目标: 让组件的调用者清楚的知道,调用组件时产生错误的原因
解决方案: 使用 prop-types 第三方包
使用步骤:
-
安装 prop-types: npm i prop-types
-
导入 prop-types 包
-
为组件添加propTypes验证
//1. 导入 prop-types 包
import PropTypes from 'prop-types'
//2. 为组件添加验证
App.propTypes = {
color: PropTypes.array
}
常见校验规则
- 常见数据类型: array、bool、func、number、object、string
- React元素类型: element
- 必填项: isRequired
- 特定结构: shape({})
import React from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'
class App extends React.Component {
render () {
console.log(this.props)
return (
<div>哈哈</div>
)
}
}
// 属性 name 类型为字符串,必填
// 属性 age 类型为数值
// 属性 hobbies 类型为数组
// 属性 firends 类型为对象 {name: 'zs', age: 170}
App.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number,
hobbies: PropTypes.array,
firends: PropTypes.shape({
name: PropTypes.string,
age: PropTypes.number
})
}
ReactDOM.render(
<App name="罗小黑" age={
18} hobbies={
['吃饭', '睡觉']} firends={
{
name:'zs', age:20}} />,
document.querySelector('#app')
)
默认值
调用组件时可以为组件设置默认值
如果调用组件时设置了属性值则使用属性值,没有设置属性值则使用默认属性值
import React from 'react'
import ReactDOM from 'react-dom'
class Pagination extends React.Component {
render () {
return (
<div>
每页显示数量: {
this.props.pagesize}
当前显示第{
this.props.pagenum}页
</div>
)
}
}
// 设置默认值
Pagination.defaultProps = {
pagenum: 1,
pagesize: 10
}
ReactDOM.render(
<Pagination pagenum={
3} />,
document.querySelector('#app')
)