【React系列】父子组件通信—props属性传值

本文来自#React系列教程:https://mp.weixin.qq.com/mp/appmsgalbum?__biz=Mzg5MDAzNzkwNA==&action=getalbum&album_id=1566025152667107329)

一. 认识组件的嵌套

组件之间存在嵌套关系:

  • 在之前的案例中,我们只是创建了一个组件App;
  • 如果我们一个应用程序将所有的逻辑都放在一个组件中,那么这个组件就会变成非常的臃肿和难以维护;
  • 所以组件化的核心思想应该是对组件进行拆分,拆分成一个个小的组件;
  • 再将这些组件组合嵌套在一起,最终形成我们的应用程序;

我们来分析一下下面代码的嵌套逻辑:

import React, { Component } from 'react';function Header() {return <h2>Header</h2>
}function Main() {return (<div><Banner/><ProductList/></div>)
}function Banner() {return <div>Banner</div>
}function ProductList() {return (<ul><li>商品1</li><li>商品2</li><li>商品3</li><li>商品4</li><li>商品5</li></ul>)
}function Footer() {return <h2>Footer</h2>
}export default class App extends Component {render() {return (<div><Header/><Main/><Footer/></div>)}
}

上面的嵌套逻辑如下,它们存在如下关系:

  • App组件是Header、Main、Footer组件的父组件;
  • Main组件是Banner、ProductList组件的父组件;

在这里插入图片描述

在开发过程中,我们会经常遇到需要组件之间相互进行通信:

  • 比如App可能使用了多个Header,每个地方的Header展示的内容不同,那么我们就需要使用者传递给Header一些数据,让其进行展示;
  • 又比如我们在Main中一次性请求了Banner数据和ProductList数据,那么就需要传递给他们来进行展示;
  • 也可能是子组件中发生了事件,需要由父组件来完成某些操作,那就需要子组件向父组件传递事件;

总之,在一个React项目中,组件之间的通信是非常重要的环节;

父组件在展示子组件,可能会传递一些数据给子组件:

  • 父组件通过 属性=值 的形式来传递给子组件数据;
  • 子组件通过 props 参数获取父组件传递过来的数据;

二. 父组件传递子组件

2.1. 子组件是class组件

我们这里先演示子组件是class组件:

import React, { Component } from 'react';// 1.类子组件
class ChildCpn1 extends Component {constructor(props) {super();this.props = props;}render() {const { name, age, height } = this.props;return (<div><h2>我是class的组件</h2><p>展示父组件传递过来的数据: {name + " " + age + " " + height}</p></div>)}
}export default class App extends Component {render() {return (<div><ChildCpn1 name="why" age="18" height="1.88" /></div>)}
}

按照上面的结构,我们每一个子组件都需要写构造器来完成:this.props = props;

其实呢,大可不必,因为我们可以调用super(props),我们来看一下Component的源码:

function Component(props, context, updater) {this.props = props;this.context = context;// If a component has string refs, we will assign a different object later.this.refs = emptyObject;// We initialize the default updater but the real one gets injected by the// renderer.this.updater = updater || ReactNoopUpdateQueue;
}
  • 这里我们先不关心contextupdater
  • 我们发现传入的props会被Component设置到this中(父类的对象),那么子类就可以继承过来;

所以我们的构造方法可以换成下面的写法:

constructor(props) {super(props);
}

甚至我们可以省略,为什么可以省略呢?

如果不指定构造方法,则使用默认构造函数。对于基类,默认构造函数是:

constructor() {}

对于派生类,默认构造函数是:

constructor(...args) {super(...args);
}

为什么 constructor 中不是必须传入 props 也能使用

在进行React开发中,有一个很奇怪的现象:

  • 在调用super的时候,我没有传入props,但是在下面的render函数中我依然可以使用;
  • 如果你自己编写一个基础的类,可以尝试一下:这种情况props应该是undefined的;
class ChildCpn extends Component {constructor(props) {super();}render() {const {name, age, height} = this.props;return (<h2>子组件展示数据: {name + " " + age + " " + height}</h2>)}
}

为什么这么神奇呢?

  • 我一直喜欢说:计算机中没有黑魔法;
  • 之所以可以,恰恰是因为React担心你的代码会出现上面这种写法而进行了一些骚操作
  • React不管你有没有通过superprops设置到当前的对象中,它都会重新给你设置一遍;

如何验证呢?

  • 这就需要通过源码来验证了;
  • React的源码packages中有提供一个Test Rendererpackage
  • 这个 package 提供了一个 React 渲染器,用于将 React 组件渲染成纯 JavaScript 对象,不需要依赖 DOM 或原生移动环境;

查看源码:

在这里插入图片描述

我们来看一下这个组件是怎么被创建出来的:

  • 我们找到其中的render函数;
    在这里插入图片描述
  • render函数中有这样的一段代码;
    在这里插入图片描述
  • 这个_instance实例就是组件对象;
  • 我们再看一下,它在哪里重新赋值的:
    在这里插入图片描述
  • 这里还包括通过this._instance的方式回调生命周期函数;
  • 这里可以看到是在组件挂载之前(componentWillMount方法调用之前)就进行了props的保存,因此可以在rendercomponentDidMount生命周期方法中获取到props的值。

结论不管你写不写构造函数,甚至不传super(props)的情况下,React源码中都会主动帮你进行props属性的绑定骚操作,会绑定到当前调用的对象实例的this上。

2.2. 子组件是function组件

我们再来演练一下,如果子组件是一个function组件:

function ChildCpn2(props) {const {name, age, height} = props;return (<div><h2>我是function的组件</h2><p>展示父组件传递过来的数据: {name + " " + age + " " + height}</p></div>)
}export default class App extends Component {render() {return (<div><ChildCpn1 name="why" age="18" height="1.88"/><ChildCpn2 name="kobe" age="30" height="1.98"/></div>)}
}

functional组件相对来说比较简单,因为不需要有构造方法,也不需要有this的问题。

其实本质还是通过 props 传递事件点击函数而已

2.3. 参数验证 propTypes

对于传递给子组件的数据,有时候我们可能希望进行验证,特别是对于大型项目来说:

  • 当然,如果你项目中默认继承了Flow或者TypeScript,那么直接就可以进行类型验证;
  • 但是,即使我们没有使用Flow或者TypeScript,也可以通过 prop-types 库来进行参数验证;

从 React v15.5 开始,React.PropTypes 已移入另一个包中:prop-types

我们对之前的class组件进行验证:

ChildCpn1.propTypes = {name: PropTypes.string,age: PropTypes.number,height: PropTypes.number
}

这个时候,控制台就会报警告:

在这里插入图片描述

<ChildCpn1 name="why" age={18} height={1.88}/>

更多的验证方式,可以参考官网:https://zh-hans.reactjs.org/docs/typechecking-with-proptypes.html

  • 比如验证数组,并且数组中包含哪些元素;
  • 比如验证对象,并且对象中包含哪些key以及value是什么类型;
  • 比如某个原生是必须的,使用 requiredFunc: PropTypes.func.isRequired

如果没有传递,我们希望有默认值呢?

  • 我们使用defaultProps就可以了
ChildCpn1.defaultProps = {name: "王小波",age: 40,height: 1.92
}

三. 子组件传递父组件

某些情况,我们也需要子组件向父组件传递消息:

  • 在vue中是通过自定义事件来完成的;
  • 在React中同样是通过props传递消息,只是让父组件给子组件传递一个回调函数,在子组件中调用这个函数即可;

我们这里来完成一个案例:

  • 将计数器案例进行拆解;
  • 将按钮封装到子组件中:CounterButton
  • CounterButton发生点击事件,将内容传递到父组件中,修改counter的值;

在这里插入图片描述
案例代码如下:

import React, { Component } from 'react';function CounterButton(props) {const { operator, btnClick } = props;return <button onClick={btnClick}>{operator}</button>
}export default class App extends Component {constructor(props) {super(props);this.state = {counter: 0}}changeCounter(count) {this.setState({counter: this.state.counter + count})}render() {return (<div><h2>当前计数: {this.state.counter}</h2><CounterButton operator="+1" btnClick={e => this.changeCounter(1)} /><CounterButton operator="-1" btnClick={e => this.changeCounter(-1)} /></div>)}
}

四. 组件通信案例练习

我们来做一个相对综合的练习:

在这里插入图片描述

index.js代码:

import React from "react";
import ReactDOM from 'react-dom';
import "./style.css";import App from './App';ReactDOM.render(<App/>, document.getElementById("root"));

App.js

import React, { Component } from 'react';import TabControl from './TabControl';export default class App extends Component {constructor(props) {super(props);this.titles = ["流行", "新款", "精选"];this.state = {currentTitle: "流行"}}itemClick(index) {this.setState({currentTitle: this.titles[index]})}render() {return (<div><TabControl titles={this.titles} itemClick={index => this.itemClick(index)} /><h2>{this.state.currentTitle}</h2></div>)}
}

TabControl.js

import React, { Component } from 'react'export default class TabControl extends Component {constructor(props) {super(props);this.state = {currentIndex: 0}}render() {const {titles} = this.props;const {currentIndex} = this.state;return (<div className="tab-control">{titles.map((item, index) => {return (<div className="tab-item" onClick={e => this.itemClick(index)}><span className={"title " + (index === currentIndex ? "active": "")}>{item}</span></div>)})}</div>)}itemClick(index) {this.setState({currentIndex: index});this.props.itemClick(index);}
}

style.css

.tab-control {height: 40px;line-height: 40px;display: flex;
}.tab-control .tab-item {flex: 1;text-align: center;
}.tab-control .title {padding: 3px 5px;
}.tab-control .title.active {color: red;border-bottom: 3px solid red;
}

五. React插槽实现

5.1. 为什么使用插槽?

在开发中,我们抽取了一个组件,但是为了让这个组件具备更强的通用性,我们不能将组件中的内容限制为固定的div、span等等这些元素。

我们应该让使用者可以决定某一块区域到底存放什么内容。

举个栗子:假如我们定制一个通用的导航组件 - NavBar

  • 这个组件分成三块区域:左边-中间-右边,每块区域的内容是不固定;
  • 左边区域可能显示一个菜单图标,也可能显示一个返回按钮,可能什么都不显示;
  • 中间区域可能显示一个搜索框,也可能是一个列表,也可能是一个标题,等等;
  • 右边可能是一个文字,也可能是一个图标,也可能什么都不显示;

在这里插入图片描述

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

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

我这里先提前给出NavBar的样式:

.nav-bar {display: flex;height: 44px;line-height: 44px;text-align: center;
}.nav-bar .left, .nav-bar .right {width: 80px;background: red;
}.nav-bar .center {flex: 1;background: blue;
}

5.2. children实现

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

比如:

<Welcome>Hello world!</Welcome>

Welcome 组件中获取 props.children,就可以得到字符串 Hello world!

function Welcome(props) {return <p>{props.children}</p>;
}

当然,我们之前看过props.children的源码:

  • 如果只有一个元素,那么children指向该元素;
  • 如果有多个元素,那么children指向的是数组,数组中包含多个元素;

那么,我们的NavBar可以进行如下的实现:

import React, { Component } from 'react';class NavBar extends Component {render() {return (<div className="nav-bar"><div className="item left">{this.props.children[0]}</div><div className="item center">{this.props.children[1]}</div><div className="item right">{this.props.children[2]}</div></div>)}
}export default class App extends Component {render() {return (<div><NavBar><div>返回</div><div>购物街</div><div>更多</div></NavBar></div>)}
}

5.3. props实现

通过children实现的方案虽然可行,但是有一个弊端:通过索引值获取传入的元素很容易出错,不能精准的获取传入的原生;

另外一个种方案就是使用 props 实现:

  • 通过具体的属性名,可以让我们在传入和获取时更加的精准;
import React, { Component } from 'react';class NavBar extends Component {render() {const { leftSlot, centerSlot, rightSlot } = this.props;return (<div className="nav-bar"><div className="item left">{leftSlot}</div><div className="item center">{centerSlot}</div><div className="item right">{rightSlot}</div></div>)}
}export default class App extends Component {render() {const navLeft = <div>返回</div>;const navCenter = <div>购物街</div>;const navRight = <div>更多</div>;return (<div><NavBar leftSlot={navLeft} centerSlot={navCenter} rightSlot={navRight} /></div>)}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/597746.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于粒子群算法的参数拟合,寻优算法优化测试函数

目录 摘要 测试函数shubert 粒子群算法的原理 粒子群算法的主要参数 粒子群算法原理 粒子群算法参数拟合 代码 结果分析 展望 基于粒子群算法的参数拟合(代码完整,数据齐全)资源-CSDN文库 https://download.csdn.net/download/abc991835105/88698417 摘要 寻优算法,测试…

后端开发——JDBC的学习(三)

本篇继续对JDBC进行总结&#xff1a; ①通过Service层与Dao层实现转账的练习&#xff1b; ②重点&#xff1a;由于每次使用连接就手动创建连接&#xff0c;用完后就销毁&#xff0c;这样会导致资源浪费&#xff0c;因此引入连接池&#xff0c;练习连接池的使用&#xff1b; …

x-cmd pkg | tig - git 文本模式界面

目录 简介首次用户功能特点类似工具与竞品进一步探索 简介 tig 由 Jonas Fonseca 于 2006 年使用 C 语言创建的 git 交互式文本命令行工具。旨在开启交互模式快速浏览 git 存储库的信息以及 git 命令的运行。 首次用户 使用 x tig 即可自动下载并使用 在终端运行 eval "…

微服务之间互相调用出现的错误

场景&#xff1a; 微服务A调用微服务B的接口&#xff0c;微服务B的接口请求方式是get类型&#xff0c;传递的参数是JSON格式。 错误&#xff1a; 1、postman&#xff1a;springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserial…

Gin 框架介绍与快速入门

Gin 框架介绍与快速入门 文章目录 Gin 框架介绍与快速入门一、Gin框架介绍1. 快速和轻量级2. 路由和中间件3. JSON解析4. 支持插件5. Gin相关文档 二、基本使用1.安装2.导入3.第一个Gin 应用 三、应用举例四、Gin 入门核心1.gin.Engine2.gin.Context 一、Gin框架介绍 Gin是一个…

VitePress搭建Vite官方中文文档首页

✨专栏介绍 在当今数字化时代&#xff0c;Web应用程序已经成为了人们生活和工作中不可或缺的一部分。而要构建出令人印象深刻且功能强大的Web应用程序&#xff0c;就需要掌握一系列前端技术。前端技术涵盖了HTML、CSS和JavaScript等核心技术&#xff0c;以及各种框架、库和工具…

FinGPT——金融领域开源大模型

文章目录 背景论文摘要相关工作大型语言模型&#xff08;LLMs&#xff09;和ChatGPT金融领域的LLMs为什么需要开源的金融LLMs&#xff1f; 以数据为中心的方法用于FinLLMs金融数据和独特特性应对处理金融数据的挑战 FINGPT 概述&#xff1a;FINLLM 的开源框架数据来源面向金融N…

【常用排序算法】冒泡排序

冒泡排序 冒泡排序基本思想&#xff1a;N 个数的数组&#xff0c;经过N-1轮排序。 升序 大的值下沉&#xff0c;小的值上浮。降序 小的值下沉&#xff0c;小的字上浮 import java.util.Arrays; public class BubbleSort {public static void main(String[] args) {int[] values…

Git 对项目更新的时候提示错误 repository not owned by current user

遇到 Git 提示的错误信息为&#xff1a;repository not owned by current user 上图显示的是错误的信息。 问题和解决 出现上面错误信息的原因是当前文件夹的权限和 Git 的执行权限不一直导致的。 我们的问题是我们希望在网盘上使用 Git 更新克隆后的代码&#xff0c;但登录…

React Hook 原理,及如何使用Hook

一、 Hook使用规则 只在最顶层使用Hook 不要在循环&#xff0c;条件或嵌套函数中调用Hook&#xff1b; 只在组件函数和自定义hook中调用Hook Q1 &#xff1a; 为什么 hook 不能 在循环&#xff0c;条件或嵌套函数中调用Hook &#xff1f; A1&#xff1a; 因为这跟React的…

使用Kafka与Spark Streaming进行流数据集成

在当今的大数据时代&#xff0c;实时数据处理和分析已经变得至关重要。为了实现实时数据集成和分析&#xff0c;组合使用Apache Kafka和Apache Spark Streaming是一种常见的做法。本文将深入探讨如何使用Kafka与Spark Streaming进行流数据集成&#xff0c;以及如何构建强大的实…

【智慧地球】星图地球 | 星图地球超算数据工场

当前空天信息处理涉及并发并行的大量计算问题&#xff0c;需要高性能计算、智能计算联合调度&#xff0c;以此来实现多算力融合&#xff1b;而我国算力产业规模快速增长&#xff0c;超算算力资源正需要以任务驱动来统筹。 基于此&#xff0c;中科星图与郑州中心展开紧密合作&a…

从0开始python学习-39.requsts库

目录 HTTP协议 1. 请求 2. 响应 Requests库 1. 安装 2. 请求方式 2.1 requests.请求方式(参数) 2.2 requests.request() 2.3 requests.session().request() 2.4 三种方式之间的关联 3. 请求参数 3.1 params&#xff1a;查询字符串参数 3.2 data&#xff1a;Form表单…

【Python可视化实战】钻石数据可视化

一、项目引言 1.背景和目标 钻石作为一种珍贵的宝石&#xff0c;其价格受到多种因素的影响。为了深入了解钻石价格的决定因素&#xff0c;我们收集了大量关于钻石的数据&#xff0c;并希望通过数据可视化来揭示钻石特征与价格之间的关系。 2.内容 收集钻石的各项特征数据&a…

【大数据】分布式协调系统 Zookeeper

分布式协调系统 Zookeeper 1.Zookeeper 的特点2.Zookeeper 的数据结构3.Zookeeper 的应用场景3.1 统一命名服务3.2 统一配置管理3.3 统一集群管理3.4 服务器动态上下线3.5 软负载均衡 Zookeeper 是 Apache 开源的一个顶级项目&#xff0c;目的是为分布式应用提供协调服务&#…

IO进程线程 day4

进程状态间的转化 创建出三个进程完成两个文件之间拷贝工作&#xff0c;子进程1拷贝前一半内容&#xff0c;子进程2拷贝后一半内容&#xff0c;父进程回收子进程的资源 #include <head.h> int main(int argc, const char *argv[]) {FILE *fp1NULL,*fp2NULL;//定义两个文…

【Java基础篇】常见的字符编码、以及它们的区别

常见的字符编码、以及它们的区别 ✔️ 解析✔️扩展知识仓✔️Unicode和UTF-8有啥关系?✔️有了UTF-8&#xff0c;为什么要出现GBK✔️为什么会出现乱码 ✔️ 解析 就像电报只能发出 ”滴” 和 ”答” 声一样&#xff0c;计算机只认识 0 和 1 两种字符&#xff0c;但是&#x…

【驱动序列】C#获取电脑硬件基本组合以及基础信息

大家好&#xff0c;我是全栈小5&#xff0c;欢迎阅读《小5讲堂之知识点实践序列》文章。 这是2024年第7篇文章&#xff0c;此篇文章是C#知识点实践序列文章&#xff0c;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 要开发一款驱动小助手&…

vue封装基础input组件(添加防抖功能)

先看一下效果&#xff1a; // 调用页面 <template><div><!-- v-model&#xff1a;伪双向绑定 --><my-input v-model"inputVal" label"姓名" type"textarea" /></div> </template><script> import…

第3章 【例题】(完整版)

目录 前言 【例3.1】有关成绩结构体的例子 【例3.2】使用Score类的完整程序 【例 3.3】一个存在错误的程序 【例3.4】用对象赋值语句的例子 【例3.5】为类Score定义一个构造函数 【例3.6】建立对象的同时&#xff0c;用构造函数给数据成员赋初值 【例3.7】用成员初始…