react基础--组件通讯:props基础、子传父、父传子、兄弟组件通讯、context跨级组件、props进阶

目录

一、props基础

1.1 概述

1.2 函数组件通讯

1.2.1 基本用法 

1.2.1 对象数据传递

 1.3 类组件通讯

1.4  props的特点

二、组件通讯三种方式

2.1 父传子

2.2 子传父 

2.3 兄弟组件通讯

三、context跨级组件

实现思路:

四、props进阶

4.1 children属性

4.2  props校验

4.2.1 类组件中

4.2.2 函数组件

4.3 约束规则

4.4 props默认值

4.4.1 类组件中使用props默认值

4.4.2 函数组件中使用props默认值


react基础--组件通讯:props基础、子传父、父传子、兄弟组件通讯、context跨级组件、props进阶_第1张图片

组件是独立且封闭的单元,默认情况下,只能使用组件自己的数据。

在组件化过程中,我们将一个完整的功能 拆分成多个组件,以更好的完成整个应用的功能。

而在这个过程中,多个组件之间不可避免的要共享某些数据 。

为了实现这些功能,就需要打破组件的独立封闭性,让其与外界沟通。这个过程就是组件通

一、props基础

1.1 概述

  • 组件是封闭的,要接收外部数据应该通过props来实现

  • props的作用:接收传递给组件的数据

  • 传递数据:给组件标签添加属性

  • 接收数据:函数组件通过参数props接收数据,类组件通过this.props接收数据


1.2 函数组件通讯

1.2.1 基本用法 

子组件

import React from 'react'

export default function Props(props) {

    console.log(props);

  return (
    
标题:{props.title}
) }

index.js--父组件

import Props from "./components/Props";

function App() {
  return (
    
); } export default App;

1.2.1 对象数据传递

如果子组件接收的数据是一个一个的字段,在对象中有很多数据需要传入,那么你可以使用es6中的扩展语法将对象的数据全部扩展进去

子组件Props.jsx

import React from 'react'

export default function Props(props) {

    console.log(props);

  return (
    
标题:{props.title}
姓名:{props.name}
年龄:{props.age}
地址:{props.address}
) }

父组件App.jsx

import Props from "./components/Props";


let user = {
  name: '张三',
  age: 20,
  address: '红旗河沟'
}

function App() {
  return (
    
); } export default App;

 

如果子组件接收的是整个对象,那么你可以直接将对象传入

子组件Props.jsx

import React from 'react'

export default function Props(props) {

    console.log(props);

  return (
    
标题:{props.title}
姓名:{props.user.name}
年龄:{props.user.age}
地址:{props.user.address}
) }

父组件App.jsx

import Props from "./components/Props";


let user = {
  name: '张三',
  age: 20,
  address: '红旗河沟'
}

function App() {
  return (
    
); } export default App;


 1.3 类组件通讯

类组件的外部数据通过this.props来访问

子组件---接收数据

import React, { Component } from 'react'

export default class PropsClass extends Component {
    constructor(props) {
        super(props);
    }
  render() {
    return (
      
标题:{this.props.title}
) } }

父组件--传递数据

import Props from "./components/Props";

function App() {
    return (
        
); } export default App;

1.4  props的特点

  • 可以给组件传递任意类型的数据

  • props是只读的,不允许修改props的数据

Demo.js

// 函数组件
// 方式1
export default function Demo(props) { // 或下面这种写法--解构
// export default function Demo({car, money, check, name}) {
    console.log(props);
    return (
        

Demo组件

{ /**方式1 * */ }

{props.car}

{props.money}

{props.check ? '是' : '否'}

{props.name}

子组件:{props.money}
{ // 方式2 /** *

{car}

{money}

{check ? '是' : '否'}

{name}

*/ }
) }

index.js

/**
 * 1. 导入react和react-dom
 * 2. 创建 react 元素
 * 3. 把 react 元素渲染到页面
 */
 import React from 'react';
 import ReactDom from 'react-dom/client';
 import { Component } from 'react';
 import Demo from './Demo'

 class App extends Component {
     state = {
         money: 100
     }
     render() {
         return 

APP组件

{ /** * 1. 通过属性的方式,给组件提供数据 * 2. 子组件中通过 props 就可以获得参数 */ } {console.log(1);}} list={[1,2,3]} obj = {{age: 12}} content = {
内容
} >
父组件:{this.state.money}
} Add = () => { this.setState({ money: this.state.money + 10 }) } } // 幽灵节点:节点不会渲染任何的内容,跟 vue 里面的 template 标签一样 const element = ( ); // 参数1:渲染的 react 元素即虚拟 DOM // 参数2:需要渲染到哪个容器中 const root = ReactDom.createRoot(document.getElementById('root')); root.render(element);

react基础--组件通讯:props基础、子传父、父传子、兄弟组件通讯、context跨级组件、props进阶_第2张图片

  • 注意:在类组件中使用的时候,需要把props传递给super(),否则构造函数无法获取到props

Hello.js

/**
 * 1. 导入react和react-dom
 * 2. 创建 react 元素
 * 3. 把 react 元素渲染到页面
 */
 import React from 'react';
 import { Component } from 'react';

export default class Hello extends Component {
    constructor(props) {
        super(props)
        this.state = {
            money: this.props.money + 100
        }
    }
    render() {
        console.log(this.props);
        return (
            

类组件

{this.state.money}

) } }

index.js

/**
 * 1. 导入react和react-dom
 * 2. 创建 react 元素
 * 3. 把 react 元素渲染到页面
 */
 import React from 'react';
 import ReactDom from 'react-dom/client';
 import { Component } from 'react';
 import Hello from './Hello'

 class App extends Component {
     state = {
         money: 100
     }
     render() {
         return 

APP组件

{ /** * 1. 通过属性的方式,给组件提供数据 * 2. 子组件中通过 props 就可以获得参数 */ }
} Add = () => { this.setState({ money: this.state.money + 10 }) } } // 幽灵节点:节点不会渲染任何的内容,跟 vue 里面的 template 标签一样 const element = ( ); // 参数1:渲染的 react 元素即虚拟 DOM // 参数2:需要渲染到哪个容器中 const root = ReactDom.createRoot(document.getElementById('root')); root.render(element);

react基础--组件通讯:props基础、子传父、父传子、兄弟组件通讯、context跨级组件、props进阶_第3张图片


二、组件通讯三种方式

  • 父传子

  • 子传父

  • 非父子

2.1 父传子

  1. 父组件提供要传递的state数据

  2. 给子组件标签添加属性,值为 state 中的数据

  3. 子组件中通过 props 接收父组件中传递的数据

父组件提供数据并且传递给子组件

class Parent extends React.Component {
    state = { lastName: '王' }
    render() {
        return (
            
传递数据给子组件:
) } }

子组件接收数据

function Child(props) {
	return 
子组件接收到数据:{props.name}
}


2.2 子传父 

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

  1. 父组件提供一个回调函数(用于接收数据)

  2. 将该函数作为属性的值,传递给子组件

  3. 子组件通过 props 调用回调函数

  4. 将子组件的数据作为参数传递给回调函数

父组件提供函数

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

子组件接收函数并且调用

class Child extends React.Component {
    state = { childMsg: 'React' }
    handleClick = () => {
    	this.props.getMsg(this.state.childMsg)
    }
    return (
    	
    )
}

注意:回调函数中 this 指向问题!

你要改色的文字


2.3 兄弟组件通讯

在 react 里面并没有兄弟组件通讯

但是如果在做项目的时候涉及到了兄弟组件通讯,那么就提供了一个核心的思想--状态提升

将共享状态提升到最近的公共父组件中,由公共父组件管理这个状态

react基础--组件通讯:props基础、子传父、父传子、兄弟组件通讯、context跨级组件、props进阶_第4张图片

 公共父组件职责:

  • 提供共享状态

  • 提供操作共享状态的方法

要通讯的子组件只需通过 props 接收状态或操作状态的方法

index.js

/**
 * 1. 导入react和react-dom
 * 2. 创建 react 元素
 * 3. 把 react 元素渲染到页面
 */
 import React from 'react';
 import ReactDom from 'react-dom/client';
 import { Component } from 'react';
 import Jack from "./jack";
 import Rose from "./rose"
 
 class App extends Component {
    // 1. 状态提升到父组件
    state = {
        msg: ''
    }
     render() {
         return (
           

父组件


{ // 2. 把状态给子组件显示 }
) } changeMessage = (msg) => { this.setState({ msg }) } } // 现在的写法1 // // 幽灵节点:节点不会渲染任何的内容,跟 vue 里面的 template 标签一样 // const element = ( // // // // ); // // 参数1:渲染的 react 元素即虚拟 DOM // // 参数2:需要渲染到哪个容器中 // const root = ReactDom.createRoot(document.getElementById('root')); // root.render(element); // 现在的写法2 ReactDom.createRoot(document.getElementById('root')).render() // 老版本写法 // ReactDom.render(, document.getElementById('root'))

jack.js

import React, { Component } from 'react'

export default class Jack extends Component {
  render() {
    return (
      

Jack子组件

) } say = () => { this.props.say('you jump i look') } }

rose.js

import React, { Component } from 'react'

export default class Rose extends Component {
  render() {
    return (
      

Rose子组件--{this.props.msg}

) } }

react基础--组件通讯:props基础、子传父、父传子、兄弟组件通讯、context跨级组件、props进阶_第5张图片


三、context跨级组件

思考:App 组件要传递数据给 Child 组件,该如何处理?

react基础--组件通讯:props基础、子传父、父传子、兄弟组件通讯、context跨级组件、props进阶_第6张图片

处理方式:使用 props 一层层组件往下传递(繁琐)

更好的方式:使用 Context

推荐方式:在后续可能就不用 context 了,而是用 redux

作用跨组件传递数据(比如:主题、语言等)

react基础--组件通讯:props基础、子传父、父传子、兄弟组件通讯、context跨级组件、props进阶_第7张图片

实现思路:

  1. 调用 React. createContext() 创建 Provider(提供数据) 和 Consumer(消费数据) 两个组件。
  2. 使用 Provider 组件作为父节点。
  3. 设置 value 属性,表示要传递的数据。
  4. 调用 Consumer 组件接收数据。

index.js

/**
 * 1. 导入react和react-dom
 * 2. 创建 react 元素
 * 3. 把 react 元素渲染到页面
 */
 import React from 'react';
 import ReactDom from 'react-dom/client';
 import { Component } from 'react';
import Father from "./Father"
 
// 1. 调用 React.createContext() 创建 Provider(提供数据) 
// 和 Consumer(消费数据) 两个组件
// Provider 包裹根元素,Provider 就是最大的根元素
const { Provider, Consumer } = React.createContext()
export { Consumer }
 class App extends Component {
    state = {
        color: 'green'
    }
     render() {
         return (
            
                

APP组件


) } } // 现在的写法1 // // 幽灵节点:节点不会渲染任何的内容,跟 vue 里面的 template 标签一样 // const element = ( // // // // ); // // 参数1:渲染的 react 元素即虚拟 DOM // // 参数2:需要渲染到哪个容器中 // const root = ReactDom.createRoot(document.getElementById('root')); // root.render(element); // 现在的写法2 ReactDom.createRoot(document.getElementById('root')).render() // 老版本写法 // ReactDom.render(, document.getElementById('root'))

Father.js 

import React, { Component } from 'react'
import Son from './Son'

export default class Father extends Component {
  render() {
    return (
      
父组件

) } }

Son.js

import React, { Component } from 'react'
import Sun from "./Sun";

export default class Son extends Component {
  render() {
    return (
      
子组件

) } }

Sun.js

import React, { Component } from 'react'
import { Consumer } from './index'

export default class Sun extends Component {
  render() {
    return (
      
{ (value) =>
子孙组件--{value}
}
) } }

react基础--组件通讯:props基础、子传父、父传子、兄弟组件通讯、context跨级组件、props进阶_第8张图片

index.js

/**
 * 1. 导入react和react-dom
 * 2. 创建 react 元素
 * 3. 把 react 元素渲染到页面
 */
 import React from 'react';
 import ReactDom from 'react-dom/client';
 import { Component } from 'react';
import Father from "./Father"
 
// 1. 调用 React.createContext() 创建 Provider(提供数据) 
// 和 Consumer(消费数据) 两个组件
// Provider 包裹根元素,Provider 就是最大的根元素
const { Provider, Consumer } = React.createContext()
export { Consumer }
 class App extends Component {
    state = {
        color: 'red',
        bgColor: 'skyblue'
    }
    changeColor = (color) => {
        this.setState({
            color
        })
    }
     render() {
        const { color, bgColor } = this.state
         return (
            
                

APP组件


) } } ReactDom.createRoot(document.getElementById('root')).render()

Father.js

import React, { Component } from 'react'
import Son from './Son'

export default class Father extends Component {
  render() {
    return (
      
父组件

) } }

Son.js

import React, { Component } from 'react'
import Sun from "./Sun";

export default class Son extends Component {
  render() {
    return (
      
子组件

) } }

Sun.js

import React, { Component } from 'react'
import { Consumer } from './index'

export default class Sun extends Component {
  render() {
    return (
      
{ ({color, bgColor, changeColor}) =>
子孙组件
}
) } }

react基础--组件通讯:props基础、子传父、父传子、兄弟组件通讯、context跨级组件、props进阶_第9张图片

总结:

  1. 如果两个组件是远方亲戚(比如,嵌套多层)可以使用Context实现组件通讯

  2. Context提供了两个组件:Provider 和 Consumer

  3. Provider组件:用来提供数据

  4. Consumer组件:用来消费数据


四、props进阶

4.1 children属性

表示该组件的子节点,只要组件有子节点,props就有该属性

children 属性与普通的props一样,值可以是任意值(文本、React元素、组件,甚至是函数)

index.js

function Hello(props) {
  return (
    
该组件的子节点:{props.children}
) } 我是子节点


4.2  props校验

对于组件来说,props是外来的,无法保证组件使用者传入什么格式的数据  

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

对于某些应用程序来说,你可以使用 Flow 或 TypeScript等JavaScript扩展来对整个应用程序做类型检查。但即使你不使用这些扩展,React 也内置了一些类型检查的功能。要在组件的 props 上进行类型检查,你只需配置特定的 propTypes 属性:

props校验允许在创建组件的时候,就约定props的格式、类型等

作用:规定接收的props的类型必须为数组,如果不是数组就会报错,增加组件的健壮性。

通过静态属性设置propTypes属性去校验外部数据的类型

propTypes是固定的单词,不能写错了,写错了校验不生效。

如果是老版本,你需要手动下载prop-types插件

yarn add prop-types
  1. 导入 prop-types 包

  2. 使用组件名.propTypes = {} 来给组件的props添加校验规则

  3. 校验规则通过 PropTypes 对象来指定

4.2.1 类组件中

可以在类外部设置静态属性propTypes

import React, { Component } from 'react'
import PropTypes from 'prop-types';

class PropsClass extends Component {
    constructor(props) {
        super(props);
    }
  render() {
    return (
      
标题:{this.props.title}
) } } PropsClass.propTypes = { title: PropTypes.string, isShow: PropTypes.bool } export default PropsClass;

也可以在类内部设置static静态属性propTypes

import React, { Component } from 'react'
import PropTypes from 'prop-types';

class PropsClass extends Component {

    static propTypes = {
        title: PropTypes.string,
        isShow: PropTypes.bool
    }

    constructor(props) {
        super(props);
    }
  render() {
    return (
      
标题:{this.props.title}
) } } export default PropsClass;

4.2.2 函数组件

import React from 'react'
import PropTypes from 'prop-types';

function Props(props) {

    console.log(props);

  return (
    
标题:{props.title}
姓名:{props.user.name}
年龄:{props.user.age}
地址:{props.user.address}
) } Props.propTypes = { title: PropTypes.string.isRequired } export default Props;

4.3 约束规则

  1. 常见类型:array、bool、func、number、object、string

  2. React元素类型:element

  3. 必填项:isRequired

  4. 特定结构的对象:shape({})

// 常见类型
optionalFunc: PropTypes.func,
// 必选
requiredFunc: PropTypes.func.isRequired,
// 特定结构的对象
optionalObjectWithShape: PropTypes.shape({
	color: PropTypes.string,
	fontSize: PropTypes.number
})


4.4 props默认值

场景:分页组件 每页显示条数

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

4.4.1 类组件中使用props默认值

通过静态属性defaultProps设置外部数据的默认值

defaultProps这个单词是固定的,不能写错,写错了默认值不生效。

1)在类里面通过static设置静态属性

import React, { Component } from 'react'

class PropsClass extends Component {

     static defaultProps = {
         title: '80期'
     }

    constructor(props) {
        super(props);
    }
  render() {
    return (
      
标题:{this.props.title}
) } } export default PropsClass;

2)可以在类外部设置静态属性

import React, { Component } from 'react'

class PropsClass extends Component {

    constructor(props) {
        super(props);
    }
  render() {
    return (
      
标题:{this.props.title}
) } } PropsClass.defaultProps = { title: '80期111' } export default PropsClass;

以上两种方式是等价的,都是静态属性

4.4.2 函数组件中使用props默认值

外部数据props的默认值,需要在组件上添加静态属性defaultProps

defaultProps这个单词是固定的,不能写错,写错了默认值不生效。

import React from 'react'

function Props(props) {
  return (
    
标题:{props.title}
姓名:{props.user.name}
年龄:{props.user.age}
地址:{props.user.address}
) } Props.defaultProps = { title: '蜗牛' } export default Props;


你可能感兴趣的:(react,react.js,前端)