侧边栏壁纸
博主头像
woku博主等级

成功的路上并不拥挤

  • 累计撰写 50 篇文章
  • 累计创建 13 个标签
  • 累计收到 3 条评论

跨组件通讯

woku
2022-08-10 / 0 评论 / 0 点赞 / 68 阅读 / 4,661 字

函数组件接收props

父组件:

<Hello name="jack" age={19} />

子组件:

function Hello(props) {
    console.log(props)
    return (
    	<div>接收到数据:{props.name}</div>
    )
}

函数组件中,使用props形参来接收父组件传过来的数据

类组件接收props

父组件:

<Hello name="jack" age={19} />

子组件:

class Hello extends React.Component {
    render() {
        return (
        	<div>接收到的数据:{this.props.age}</div>
        )
    }
}

类组件中,使用this.props来接收父组件传过来的数据

props的特点

  • 可以给组件传递任意类型的数据
  • props是只读的,不允许修改props的数据
  • 注意:在类组件中使用的时候,需要把props传递给super(),否则构造函数无法获取到props

props检验

目的

校验接收的props的数据类型,增加组件的健壮性
对于组件来说,props是外来的,无法保证组件使用者传入什么格式的数据
如果传入的数据格式不对,可能会导致组件内部报错。组件的使用者不能很明确的知道错误的原因。

使用

  1. 导入 prop-types 包(react脚手架自带)
  2. 使用组件名.propTypes = {} 来给组件的props添加校验规则
  3. 校验规则通过 PropTypes 对象来指定

约束规则

  1. 常见类型:array、bool、func、number、object、string
  2. React元素类型:element
  3. 必填项:isRequired
  4. 特定结构的对象:shape({})
import PropTypes from 'prop-types'
function App(props) {
    return (
    	<h1>Hi, {props.colors}</h1>
    )
}
App.propTypes = {
    // 约定colors属性为array类型
    // 如果类型不对,则报出明确错误,便于分析错误原因
    colors: PropTypes.array
}
import { Component } from "react";
import pt from 'prop-types'
class Hello extends Component {
    constructor(props) {
        super(props)
        this.state = {
            myName: this.props.name + '111'
        }
    }
    render() {
        return (
            <div>
                <h3>Hello组件</h3>
                <p>{ this.state.myName }</p>
            </div>
        )
    }
}
Hello.propTypes = {
    // 约定name属性必传,而是是string类型的值。
    name: pt.string.isRequired,
    // 约定info属性是{list: xxx, isFirst: xxx}这种结构
    // 并且list是必传的数组类型,isFirst是必传的bool类型
    info: pt.shape({
        list: pt.array.isRequired,
        isFirst: pt.bool.isRequired
    })
}
export default Hello

props的默认值

作用:给 props 设置默认值,在未传入 props 时生效

function App(props) {
    return (
        <div>
            此处展示props的默认值:{props.pageSize}
        </div>
    )
}
// 设置默认值
App.defaultProps = {
	pageSize: 10
}
// 不传入pageSize属性
<App />

注意:上面的App.propTypesApp.defaultProps都是挂载在构造函数上,通过类或者构造函数本身才能访问的属性或者方法。我们可以使用static来定义静态属性

class App extends Component {
    render() {
        return (
            <div>我是app组件</div>
        )
    }
    static propTypes = {

    }
    static defaultProps = {

    }
}

export default App

这种现代语法需要添加额外的编译步骤才能在老版浏览器中工作。
官方props类型检查文档可查阅https://zh-hans.reactjs.org/docs/typechecking-with-proptypes.html

组件通讯三种方式

  • 父传子
  • 子传父
  • 非父子

父传子

  1. 父组件提供要传递的state数据
  2. 给子组件标签添加属性,值为 state 中的数据
  3. 子组件中通过 props 接收父组件中传递的数据
class Parent extends React.Component {
    state = { lastName: '王' }
    render() {
        return (
            <div>
                传递数据给子组件:<Child name={this.state.lastName} />
            </div>
        )
    }
}
function Child(props) {
	return <div>子组件接收到数据:{props.name}</div>
}

子传父

思路:利用回调函数,父组件提供回调,子组件调用,将要传递的数据作为回调函数的参数。

  1. 父组件提供一个回调函数(用于接收数据)
  2. 将该函数作为属性的值,传递给子组件
  3. 子组件通过 props 调用回调函数
  4. 将子组件的数据作为参数传递给回调函数

父组件提供函数并且传递给字符串

class Parent extends React.Component {
    getChildMsg = (msg) => {
        console.log('接收到子组件数据', msg)
    }
    render() {
        return (
            <div>
            	子组件:<Child getMsg={this.getChildMsg} />
            </div>
        )
    }
}

子组件接收函数并且调用

class Child extends React.Component {
    state = { childMsg: 'React' }
    handleClick = () => {
    	this.props.getMsg(this.state.childMsg)
    }
    return (
    	<button onClick={this.handleClick}>点我,给父组件传递数据</button>
    )
}

兄弟组件

  • 将共享状态提升到最近的公共父组件中,由公共父组件管理这个状态
  • 思想:状态提升
  • 公共父组件职责:
    • 提供共享状态
    • 提供操作共享状态的方法
  • 要通讯的子组件只需通过 props 接收状态或操作状态的方法

状态提升前:
状态提升01.png
状态提升后:
状态提升02.png

组件通讯-context(了解)

如果组件的嵌套的层级很深,该如何通讯?如果使用props来传递,需要经过多层组件。
App 组件要传递数据给 Child 组件,该如何处理?
.png
更好的姿势:使用 Context
作用:跨组件传递数据(比如:主题、语言等)

实现思路

  • 调用 React. createContext() 创建 Provider(提供数据) 和 Consumer(消费数据) 两个组件。
const { Provider, Consumer } = React.createContext()
  • 使用 Provider 组件作为父节点。
<Provider>
    <div className="App">
    	<Child1 />
    </div>
</Provider>
  • 设置 value 属性,表示要传递的数据。
<Provider value="pink">
  • 调用 Consumer 组件接收数据。
<Consumer>
	{data => <span>data参数表示接收到的数据 -- {data}</span>}
</Consumer>

注意:
在使用Consumer时候,要和创建的Provider是一对的关系
Consumer里面是一个函数,函数的形参是Provider提供的value数据,可以是字符串,也可以是对象等等.
函数返回的结构是子组件要渲染的结构,在子组件里,可以拿到value数据。

App.js

import React from "react";
import Father from "./components/Father";
const { Component } = require("react");
const { Provider, Consumer } = React.createContext()

class App extends Component {
  state = {
    age: 27
  }
  changeAge = data => {
    this.setState({
      age: this.state.age + data
    })
  }
  render () {
    return (
      <Provider value={{ age: this.state.age, changeAge: this.changeAge }}>
         <div>
            <h2>ROOT组件</h2>
            <Father content={<p>this is a content</p>}>
            </Father>
         </div>
      </Provider>
    )
  }
}
// 导出Consumer  在子组件获取数据用Consumer(此时Consumer和Provider是一对)
export {
  Consumer
}
export default App

Father:

import { Component } from "react";
import Son from './Son'
class Father extends Component {
    render() {
        return (
            <div>我是Father组件
                {this.props.content}
                <Son></Son>
            </div>
        )
    }
}

export default Father

Son:

import { Component } from "react";
import { Consumer } from '../App'
class Son extends Component {
    render() {
        return (
            <Consumer>
              {({ age, changeAge }) => 
                <div>我是子组件-{ age }
                   <button onClick={() => changeAge(10)}>增加年龄</button>
                </div>
              }
            </Consumer>
        )
    }
}

export default Son

总结:

  1. 如果两个组件是远方亲戚(比如,嵌套多层)可以使用Context实现组件通讯
  2. Context提供了两个组件:Provider 和 Consumer
  3. Provider组件:用来提供数据
  4. Consumer组件:用来消费数据

yuque_diagram.jpg

0

评论区