Appearance
❤ React-组件通讯
组件通讯将教我们的内容:
- 能够使用道具接收数据
- 能够实现父子组件之间的通讯
- 能够实现兄弟组件之间的通讯
- 能够给组件添加道具校验
- 能够说出生命周期常用的钩子函数
- 能够知道高阶组件的作用
1、 组件通讯介绍
组件是独立且封闭的单元,默认情况下,只能使用组件自己的数据。
在组件化过程中,我们将一个完整的功能拆分成多个组件,以更好的完成整个应用的功能。而在这个过程中,多个组件之间不可避免的要共享某些数据为了实现这些功能,就需要打破组件的独立封闭性,让其与外界沟通。这个过程就是组件通讯。
换个意思说就是,大家之间总是需要沟通的呗
2、 组件的props
组件是封闭的,要接收外部数据应该通过 props 来实现props的作用:接收传递给组件的数据传递数据:给组件标签添加属性 接收数据:函数组件通过参数props接收数据,类组件通过this.props接收数据
类组件和函数式组件传递参数进行通讯如下图所示:(推荐函数式组件)
函数式组件的方式:
js
import React from 'react';
function Hello(props) {
// 输出接收到的 props
console.log(props);
// 返回 JSX 元素
return (
<div>接收到数据: {props.name}, 年龄: {props.age}</div>
);
}
// 使用 Hello 组件并传递 props
function App() {
return (
<Hello name="jack" age={19} />
);
}
export default App;
类组件的方式:
js
import React from 'react';
class Hello extends React.Component {
render() {
return (
<div>接收到的数据: {this.props.age}</div>
);
}
}
class App extends React.Component {
render() {
return (
<div>
<Hello name="jack" age={19} />
</div>
);
}
}
export default App;
函数组件props
使用案例方法
js
import React from 'react';
import ReactDOM from 'react-dom';
// 1. 接收数据的 Hello 组件
const Hello = (props) => {
// props 是一个对象
console.log(props);
return (
<div>
<h1>props: {props.name}</h1>
</div>
);
}
// 2. 传递数据并渲染组件
ReactDOM.render(<Hello name="jack" age={19} />, document.getElementById('root'));
类组件this.props
js
import React from 'react';
import ReactDOM from 'react-dom/client';
class Hello extends React.Component {
render() {
// 打印传入的 props
console.log(this.props);
return (
<div>
<h1>props: {this.props.name}</h1>
</div>
);
}
}
// 在 React 18 中使用 createRoot
const root = ReactDOM.createRoot(document.getElementById('root'));
// 渲染 Hello 组件并传递 props
root.render(<Hello name="jack" age={18} />);
组件props三个特点:
① 可以给组件传递任意类型的数据
② props是只读的对象,只能读取属性的值,无法修改对象
③ 注意:使用类组件时,如果写了构造函数,应该将props传递给super(),否则,无法在构造函数中获取props
可以给组件传递任意类型的数据
props 是只读的对象,只能读取属性的值,无法修改对象
注意:使用类组件时,如果写了构造函数,应该将 props 传递给 super(),否则,无法在构造函数中获取到 props !
js
import React from 'react';
class Hello extends React.Component {
// 构造函数接收 props,并传递给父类构造函数
constructor(props) {
super(props); // 推荐将 props 传递给父类构造函数
}
render() {
// 使用 this.props 访问传递的数据
return <div>接收到的数据: {this.props.age}</div>;
}
}
// 使用 ReactDOM 来渲染组件,假设我们有一个 ID 为 'root' 的元素
import ReactDOM from 'react-dom/client';
const root = ReactDOM.createRoot(document.getElementById('root'));
// 渲染 Hello 组件并传递 props
root.render(<Hello age={18} />);
3、 组件通讯的三种方式
父子、子父、兄弟组件
js
import React from 'react';
import ReactDOM from 'react-dom';
// 父组件
class Parent extends React.Component {
// 定义父组件的 state
state = {
lastName: '王',
};
render() {
return (
<div className="parent">
父组件:
{/* 传递 lastName 给子组件 */}
<Child name={this.state.lastName} />
</div>
);
}
}
// 子组件
const Child = (props) => {
console.log('子组件:', props); // 打印接收到的 props
return (
<div className="child">
<p>子组件,接收到父组件的数据: {props.name}</p>
</div>
);
};
// 渲染父组件
ReactDOM.render(<Parent />, document.getElementById('root'));
(1)传递方式1
父组件提供要传递的state数据
给子组件标签添加属性,值为 state 中的数据
子组件中通过 props 接收父组件中传递的数据
js
import React from 'react';
import ReactDOM from 'react-dom';
// 父组件
class Parent extends React.Component {
// 定义父组件的 state
state = {
lastName: '王', // 设置lastName的初始值
};
render() {
return (
<div>
传递数据给子组件:
{/* 传递 state 中的 lastName 给子组件 */}
<Child name={this.state.lastName} />
</div>
);
}
}
// 子组件
function Child(props) {
return <div>子组件接收到数据: {props.name}</div>;
}
// 渲染父组件到页面
ReactDOM.render(<Parent />, document.getElementById('root'));
(2)传递方式2
子组件传递数据给父组件
思路:利用回调函数,父组件提供回调,子组件调用,将要传递的数据作为回调函数的参数
- 父组件提供一个回调函数(用于接收数据)
- 将该函数作为属性的值,传递给子组件
js
import React from 'react';
import ReactDOM from 'react-dom';
// 父组件
class Parent extends React.Component {
// 父组件方法,接收子组件数据
getChildMsg = (msg) => {
console.log('接收到子组件数据', msg);
};
render() {
return (
<div>
子组件:
{/* 将父组件的方法传递给子组件 */}
<Child getMsg={this.getChildMsg} />
</div>
);
}
}
// 子组件
function Child(props) {
// 模拟子组件发送消息
const sendMessage = () => {
props.getMsg('来自子组件的消息');
};
return (
<div>
<button onClick={sendMessage}>点击发送消息给父组件</button>
</div>
);
}
// 渲染父组件到页面
ReactDOM.render(<Parent />, document.getElementById('root'));
思路:利用回调函数,父组件提供回调,子组件调用,将要传递的数据作为回调函数的参数。
3.子组件通过 props 调用回调函数
js
import React from 'react';
import ReactDOM from 'react-dom';
// 子组件
class Child extends React.Component {
// 定义子组件的 state
state = {
childMsg: 'React',
};
// 处理点击事件,向父组件传递数据
handleClick = () => {
this.props.getMsg(this.state.childMsg); // 调用父组件传递的 getMsg 方法
};
render() {
return (
<div>
<button onClick={this.handleClick}>点我,给父组件传递数据</button>
</div>
);
}
}
// 父组件
class Parent extends React.Component {
// 父组件接收子组件的数据
getChildMsg = (msg) => {
console.log('接收到子组件的数据:', msg);
};
render() {
return (
<div>
<h1>父组件</h1>
{/* 将父组件的方法传递给子组件 */}
<Child getMsg={this.getChildMsg} />
</div>
);
}
}
// 渲染父组件到页面
ReactDOM.render(<Parent />, document.getElementById('root'));
案例
js
import React from 'react';
import ReactDOM from 'react-dom/client'; //React 18
import './index.css'
class Parent extends React.Component{
state={
lastName:'父亲',
}
getChildMsg=(msg)=>{
console.log('接收到子组件数据',msg);
this.setState({
lastName:msg
})
}
render(){
return (
<div className='parent'>
父组件:
{this.state.lastName}
<Child getMsg={this.getChildMsg}></Child>
</div>)
}
}
class Child extends React.Component{
state={
msg:'行为'
}
handleClick=()=>{
this.props.getMsg(this.state.msg);
}
render(){
return (
<div className='child'>
子组件:<button onClick={this.handleClick}> 传递数据给付组件</button>
</div>)
}
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Parent name='jack' age={18} colors={['red','green','blue']}
fn={()=>{console.log('这是一个组件传递函数')}} tag={<p>设置一个p标签</p>}/>);
(3)兄弟组件通讯
(4)context
使用步骤:
1.调用 React. createContext() 创建 Provider(提供数据)和 Consumer(消费数据)两个组件。
js
const { Provider, Consumer }= React.createContext()
2.使用 Provider 组件作为父节点。
js
<Provider>
<div className="App">
<Child1 />
</div>
</Provider>
3.设置 value 属性,表示要传递的数据。
js
<Provider value="pink">
<div className="App">
<Child1 />
</div>
</Provider>
4.在子组件中通过 Consumer 组件接收数据。
js
<Consumer>
{data => <span>data参数表示接收到的数据--{data}</span>}
</Consumer>
过程:
- 如果两个组件是远方亲戚(比如,嵌套多层)可以使用Context实现组件通讯
- Context提供了两个组件:Provider和 Consumer
- Provider组件:用来提供数据
- Consumer组件:用来消费数据
案例:
js
import React from 'react';
import ReactDOM from 'react-dom';
// 创建 Context
const { Provider, Consumer } = React.createContext();
// 子组件
class Node extends React.Component {
render() {
return (
<Consumer>
{(value) => <div style={{ color: value }}>这是 Node 组件</div>}
</Consumer>
);
}
}
// 父组件
class App extends React.Component {
render() {
return (
<Provider value="pink">
<div className="app">
<Node />
</div>
</Provider>
);
}
}
// 渲染父组件到页面
ReactDOM.render(<App />, document.getElementById('root'));
js
const child = props;
return (
<div>
<span>child > 3SSNaI6</span>
<span>是子节点 -- {data}</span>
</div>
);
4、 props深入
(1)children 属性
children 属性:表示组件标签的子节点。当组件标签有子节点时,props就会有该属性
children 属性与普通的props一样,值可以是任意值(文本、React元素、组件,甚至是函数)
js
function Hello(props){
return (<div> 组件的子节点:{props.children} </div>)
}
<Hel1o>我是子节点</He11o>
(2)props校验
对组件来说,props是外来的,无法保证组件使用者传入什么格式的数据
如果传入的数据格式不对,可能会导致组件内部报错
关键问题:组件的使用者不知道明确的错误原因
js
小明创建的组件 App
function App(props) {
const arr = props.colors;
const lis = arr.map((item, index) => <li key={index}>{item.name}</li>);
return <ul>{lis}</ul>;
}
小红使用组件 App
<App colors={[{ name: '红' }, { name: '绿' }, { name: '蓝' }]} />
👉 props校验由来
从上面可以看出,如果传入的数据格式不对,可能会导致组件内部报错,项目开发过程往往多人协同开发,这个时候就需要一个机制来约束数据格式,让使用者知道如何正确使用组件,这就是props校验
👉 props校验介绍 允许在创建组件的时候,就指定props的类型、格式等
👉 props校验作用 捕获使用组件时因为props导致的错误,给出明确的错误提示,增加组件的健壮性
propTypes使用
👉认识 propTypes 是 React 中的一种用于类型检查的机制,用于验证组件的props是否符合预期类型,也就是我们认为的传入的参数类型是否正确。
👉 版本历史
JS
React0.14版本:最初作为React的一部分引入的
React15.5版本:Reac 团队将propTypes 移到一个独立的包中,并且开始鼓励开发者通过安装 prop-types 包来使用它
👉安装使用
🍆使用步骤
JS
1. 安装包 prop-types(yarn add prop-types/ npmi props-types)
2. 导入 prop-types包
3. 使用组件名.propTypes=来给组件的props添加校验规则
上面的部分其实就是我们propTypes的代码和写法:
JS
// 完整写法
import PropTypes from 'prop-types';
const App = ({ colors }) => {
// 你的组件代码
return (
<div>
{colors.map((color, index) => (
<p key={index} style={{ color }}>
{color}
</p>
))}
</div>
);
};
App.propTypes = {
colors: PropTypes.array.isRequired, // 定义 colors 必须是数组类型并且是必需的
};
export default App;
👉错误提示
我们简单看一下下面的代码错误的时候给我们的错误提示:
JS
// 完整写法
import PropTypes from 'prop-types';
const App = ({ colors }) => {
// 你的组件代码
return (
<div>
{colors.map((color, index) => (
<p key={index} style={{ color }}>
{color}
</p>
))}
</div>
);
};
App.propTypes = {
colors: PropTypes.array.isRequired, // 定义 colors 必须是数组类型并且是必需的
};
export default App;
错误写法时候给我们的提示
JS
import PropTypes from 'prop-types';
function App(props) {
return (
<h1>Hi, {props.colors}</h1>
);
}
// 定义propTypes
App.propTypes = {
// 约定colors属性为array类型
// 如果类型不对,则报出明确错误,便于分析错误原因
colors: PropTypes.array
};
export default App;
这个时候给我们的提示
👉 常见propTypes约束
- 常见类型:array、bool、func、number、object、string
- React元素类型:element
- 必填项:isRequired
- 特定结构的对象:shape(())
👉写法
JS
// 常见类型
// 可选函数
optionalFunc: PropTypes.func,
// 必选函数
requiredFunc: PropTypes.func.isRequired,
// 特定结构的对象
optionalObjectWithShape: PropTypes.shape({
color: PropTypes.string,
fontSize: PropTypes.number
})
👉完整类型
约束地址
PropType | 描述 |
---|---|
PropTypes.string | 字符串类型 |
PropTypes.number | 数字类型 |
PropTypes.bool | 布尔类型 |
PropTypes.array | 数组类型 |
PropTypes.object | 对象类型 |
PropTypes.func | 函数类型 |
PropTypes.node | 任意可渲染的内容(比如:字符串、数字、React 元素、数组等) |
PropTypes.element | React 元素类型 |
PropTypes.instanceOf(Class) | 验证值是否为某个类的实例 |
PropTypes.oneOf([value1, value2, ...]) | 验证值是否为指定的某一个值 |
PropTypes.oneOfType([type1, type2, ...]) | 验证值是否匹配多个类型中的任意一个 |
PropTypes.arrayOf(type) | 验证数组中的每一项是否符合指定类型 |
PropTypes.objectOf(type) | 验证对象的每个值是否符合指定类型 |
PropTypes.shape({}) | 验证对象的属性是否符合指定类型 |
PropTypes.any | 任意类型 |
👉案例
JS
import React from 'react';
import PropTypes from 'prop-types';
import ReactDOM from 'react-dom';
const App = (props) => {
return (
<div>
<h1>props 校验:</h1>
</div>
);
};
// 添加 props 校验
App.propTypes = {
a: PropTypes.number, // 属性 a 的类型:数值 (number)
fn: PropTypes.func.isRequired, // 属性 fn 的类型:函数 (func) 并且为必填项
tag: PropTypes.element, // 属性 tag 的类型:React 元素 (element)
filter: PropTypes.shape({ // 属性 filter 的类型:对象
area: PropTypes.string,
price: PropTypes.number,
}),
};
ReactDOM.render(<App />, document.getElementById('root'));
👉默认值defaultProps
有时候我们需要一些默认值,当父组件没有传递 props 时,我们使用默认值defaultProps。
场景:分页组件每页显示条数 作用:给props设置默认值,在未传入props时生效
JS
import React from 'react';
import ReactDOM from 'react-dom';
function App(props) {
return (
<div>
此处展示props的默认值:{props.pagesize}
</div>
);
}
// 设置默认值
App.defaultProps = {
pagesize: 10, // 默认值为 10
};
ReactDOM.render(<App />, document.getElementById('root'));