引言:
React中的组件分为两种:
- 内置DOM标签:由React提供的所有HTML/SVG标签。
- 自定义组件:开发者自己定义的组件,包括函数组件和类组件。
在React中,组件之间传递数据是构建应用时非常常见的需求。这篇文章我们就来介绍几种不同场景下的传值方法。
父子传值
当父组件需要向子组件传递数据时,可以通过props来实现,且父组件传给子组件的数据都是单向只读的。
我们来举个例子:
这里我们有一个People组件,用来进行名单信息展示
js
//通过props向子组件传递数据
function People(props) {
return (
<div>
<h2>{props.name}</h2>
<h3>{props.age}</h3>
</div>
);
}
export default function List() {
return (
<div>
<h2>List</h2>
<People name="姓名1" age={21} ></People>
<People name="姓名2" age={22} ></People>
<People name="姓名3" age={23} ></People>
</div>
);
}
分析:
- 父组件 (
List
) 通过props
向子组件 (People
) 传递数据 - 子组件通过函数参数接收这些
props
,并使用解构赋值从中提取出name
和age
。 - 这些值被用来在子组件的JSX中进行渲染。
注意:
我们要注意父组件给子组件传值中,是通过props
进行传递的,People
组件在使用name
和age
时没有正确地从props
中解构出来。运行会报错,因为name
和age
变量在People
组件的作用域内是未定义的
js
function People(props) {
return (
<div>
<h2>{name}</h2> //没有从 props中解构出name和age
<h3>{age}</h3>
</div> );
}
正确写法:
js
function People(props) {
// 从 props 中解构出 name 和 age
const { name, age } = props;
return (
<div>
<h2>{name}</h2> // 显示传递进来的名字
<h3>{age}</h3> // 显示传递进来的年龄
</div>
);
}
js
//使用ES6提供的解构功能,相当于声明了一个区域变量
function People({ name, age }) {
return (
<div>
<h2>{name}</h2>
<h3>{age}</h3>
</div>
);
}
子父传值
由于父向子传值的数据是单向的,但是我有希望一个子组件的数据能够传递到父组件,这里就要了解子向父传值了。
一般的思路是通过父组件给子组件进行一个自定义事件的设置,通过事件触发向我们的父组件进行传值。
我们通过一个小小情景剧来学习一下:
当然,让我们通过一个情景剧来解释这个过程。假设小宝(子组件)考完试回家向父母(父组件)汇报成绩。
情景剧:小宝汇报成绩
角色
- 小宝 :子组件
Child
- 父母 :父组件
Parent
场景设定
- 小宝考完试回家,他的心情取决于考试的成绩。
- 父母根据小宝的汇报给予相应的评价。
代码实现
jsx
import React, { useState } from 'react';
// 子组件 Child (小宝)
function Child({ childMood }) {
const [score, setScore] = useState(false); // 初始状态为没发挥好
function handleClick() {
const newScore = !score; // 切换成绩状态
setScore(newScore);
childMood(newScore); // 向父母汇报新的成绩
}
return (
<div
style={{
backgroundColor: score ? "blue" : "red",
}}
>
<p>
{score === false ? (
<button onClick={handleClick}>没发挥好</button>
) : (
<button onClick={handleClick}>拿了奖状!</button>
)}
</p>
</div>
);
}
// 父组件 Parent (父母)
export default function Parent() {
function comment(status) {
if (status) {
console.log("小宝真棒!");
} else {
console.log("小宝辛苦了");
}
}
return (
<div>
<h2>Parent (父母)</h2>
<Child childMood={comment} />
</div>
);
}
代码直译版解释
1. 子组件 Child
- 函数组件 :
Child
是一个函数组件,它接收一个属性childMood
,这是一个从父组件传递过来的回调函数。 - 状态管理 :使用
useState
来管理score
状态,初始值为false
。 - 事件处理 :
handleClick
函数在按钮点击时被调用,它会:- 切换
score
的状态。 - 调用
childMood
回调函数,并将新的score
状态传递给父组件。
- 切换
- 返回JSX :根据
score
的状态,背景颜色和按钮文本会发生变化。- 如果
score
是false
,背景颜色为红色,按钮文本为"没发挥好"。 - 如果
score
是true
,背景颜色为蓝色,按钮文本为"拿了奖状!"。
- 如果
2. 父组件 Parent
- 函数组件 :
Parent
是一个函数组件。 - 回调函数 :定义了一个名为
comment
的函数,它根据传入的status
参数打印不同的消息到控制台。- 如果
status
为true
,打印"小宝真棒!"。 - 如果
status
为false
,打印"小宝辛苦了"。
- 如果
- 渲染子组件 :
Parent
组件渲染了一个Child
组件,并将comment
函数作为childMood
属性传递给Child
组件。
父子传值过程
-
父组件创建子组件实例:
- 在
Parent
组件中,我们创建了一个Child
组件实例,并将comment
函数作为childMood
属性传递给Child
组件。
- 在
-
传递属性:
Parent
组件通过<Child childMood={comment} />
将comment
函数传递给Child
组件。
-
子组件接收并使用属性:
Child
组件通过函数参数接收props
对象,并从中解构出childMood
。- 当用户点击按钮时,
handleClick
函数被调用,切换score
状态,并调用childMood
函数,将新的score
状态传递给父组件。
-
父组件响应子组件的事件:
Parent
组件中的comment
函数接收到子组件传递的score
状态,并根据这个状态打印相应的消息到控制台。
情景剧版解释(纯个人理解,理解有误请指出)
第一幕:小宝回家
- 小宝 (子组件
Child
):小宝带着考试的心情回到了家。他有一个初始的心情状态(score
),表示他是否考得好。 - 父母 (父组件
Parent
):父母在家里等待小宝回来,并准备根据小宝的成绩给予反馈。
第二幕:小宝汇报成绩
-
小宝:小宝按下按钮向父母汇报自己的成绩。
- 如果成绩不好(
score
为false
),按钮显示"没发挥好"。 - 如果成绩好(
score
为true
),按钮显示"拿了奖状!"。
- 如果成绩不好(
-
点击按钮:
- 小宝点击按钮,触发
handleClick
函数。 handleClick
函数会切换score
的状态,并调用childMood
函数将新的score
状态传递给父母。
- 小宝点击按钮,触发
-
背景颜色变化:
- 如果
score
为false
,背景颜色为红色。 - 如果
score
为true
,背景颜色为蓝色。
- 如果
第三幕:父母的反馈
- 父母 :父母接收到小宝的汇报(
score
状态),并通过comment
函数给出反馈。- 如果
score
为true
,父母在控制台打印"小宝真棒!"。 - 如果
score
为false
,父母在控制台打印"小宝辛苦了"。
- 如果
详细步骤
-
初始状态:
- 小宝回家时,
score
初始值为false
,表示没发挥好。 - 背景颜色为红色,按钮文本为"没发挥好"。
- 小宝回家时,
-
第一次汇报:
- 小宝点击"没发挥好"按钮。
handleClick
函数被调用,score
从false
变为true
。childMood
函数被调用,传递新的score
状态true
给父母。- 父母接收到
score
为true
,在控制台打印"小宝真棒!"。 - 背景颜色变为蓝色,按钮文本变为"拿了奖状!"。
-
第二次汇报:
- 小宝再次点击"拿了奖状!"按钮。
handleClick
函数被调用,score
从true
变为false
。childMood
函数被调用,传递新的score
状态false
给父母。- 父母接收到
score
为false
,在控制台打印"小宝辛苦了"。 - 背景颜色变为红色,按钮文本变为"没发挥好"。
小结
- 小宝 (子组件
Child
)通过点击按钮来改变自己的成绩状态,并通过childMood
回调函数将新的成绩状态传递给父母。 - 父母 (父组件
Parent
)通过comment
函数接收小宝的成绩状态,并根据成绩状态给出相应的反馈。
兄弟传值
对于同级组件之间进行的传值我们可以通过父组件进行中转,我们还是来看例子
还是小宝,没错,这是个连续剧。。。
小宝想告诉正在外地上大学的哥哥自己考了100分的这件事,但是自己没有手机不能直接联系,这时候就要向父母求助了。
代码解释
1. Child1
组件(哥哥)
jsx
function Child1(props) {
return <div>哥哥接收到的信息 --- {props.received}</div>;
}
- 函数组件 :
Child1
是一个函数组件。 - 显示消息 :从
props
中接收received
属性,并将其显示在页面上。
2. Child2
组件(小宝)
jsx
function Child2(props) {
const score = "小宝考了一百分!";
return (
<div>
小宝---<button onClick={() => props.tellParent(score)}>告诉哥哥</button>
</div>
);
}
- 函数组件 :
Child2
是一个函数组件。 - 消息变量 :定义了一个常量
score
,其值为"小宝考了一百分!"。 - 按钮点击事件 :当用户点击按钮时,会调用从父组件传递过来的
tellParent
回调函数,并将score
作为参数传递给它。
3. Parent
组件(父母)
jsx
import { useState } from "react";
export default function Parent() {
const [message, setMessage] = useState();
const getData = (data) => {
setMessage(data);
};
return (
<div>
<h2>Parent</h2>
{console.log({ score })}
<Child1 received={message}></Child1>
<Child2 tellParent={getData}></Child2>
</div>
);
}
- 函数组件 :
Parent
是一个函数组件。 - 状态管理 :使用
useState
来管理一个名为message
的状态变量。 - 回调函数 :定义了一个名为
getData
的回调函数,该函数接收一个参数data
,并使用setMessage
更新message
状态。 - 渲染子组件 :
- 渲染
Child1
组件,并将message
状态作为received
属性传递给它。 - 渲染
Child2
组件,并将getData
函数作为tellParent
属性传递给它。
- 渲染
情景剧剧本
第一幕:初始状态
- Parent (父母):初始化状态
message
为空。 - Child1(哥哥):等待接收信息。
- Child2(小宝):等待用户点击按钮。
第二幕:小宝告诉哥哥成绩
- 用户 :点击
Child2
中的按钮"告诉哥哥"。 - Child2 :调用从
Parent
传递过来的tellParent
回调函数,并将score
("小宝考了一百分!")传递给Parent
。 - Parent :接收到
score
,并通过getData
函数更新message
状态为"小宝考了一百分!"。 - Child1 :接收到更新后的
message
状态,并显示"小宝考了一百分!"。
详细步骤
-
初始状态:
Parent
组件中的message
状态初始为空。Child1
组件显示空消息。Child2
组件显示按钮"告诉哥哥"。
-
点击按钮:
- 用户点击
Child2
中的按钮"告诉哥哥"。 Child2
调用tellParent
函数,并传递score
("小宝考了一百分!")给Parent
。
- 用户点击
-
更新状态:
Parent
接收到score
,并通过getData
函数更新message
状态为"小宝考了一百分!"。
-
显示消息:
Child1
接收到更新后的message
状态,并显示"小宝考了一百分!"。
小结
- Child2 (小宝)通过点击按钮调用从
Parent
传递过来的回调函数tellParent
,并将消息传递给Parent
。 - Parent (父母)接收到消息后,更新状态
message
。 - Child1 (哥哥)通过
Parent
传递的message
状态显示消息。
祖孙传值
由于祖孙传值的方式有多种,这里我们就不采用情景剧进行模拟了。
在React中,祖孙组件传值可以通过以下几种方式实现:
1. 通过props进行传值
通常在比较简单的情况,祖父组件会通过props将数据传递给父组件,父组件再将其传递给子组件。以下是一个隔代向子组件传值的简单示例:
jsx
//Grandfather.jsx
import React from "react";
const Grandfather = () => {
const data = "Grandfather的数据";
return (
<div>
<h2>Grandfather</h2>
{data}
</div>
);
};
export default Grandfather;
jsx
// Father.jsx
import React from "react";
import Grandfather from "./Grandfather";
export default function Father(props) {
return (
<div>
<h3>Father</h3>
<Grandfather data={props.data}></Grandfather>
</div>
);
}
jsx
// Son.jsx
import React from "react";
import Father from "./Father";
export default function Son(props) {
return (
<div>
<h4>Son</h4>
<Father data={props.data} />
</div>
);
}
2. 使用Context API
如果数据需要在多个层级的组件之间共享,可以使用Context API。
核心概念
createContext()
createContext
是 React 中的一个函数,它用于创建一个 Context
对象。这个对象包括两个主要的部分:
- Provider(提供者)
- Consumer(消费者)
jsx
const MyContext = React.createContext();
Provider(数据提供者)
Provider
是用来提供数据的组件。它接收一个 value
属性,所有包裹在 Provider
组件内的组件都可以访问到这个 value
,无论它们在组件树中有多深。
jsx
<MyContext.Provider value={/* 传递的值 */}>
{/* 子组件 */}
</MyContext.Provider>
Consumer(数据消费者)
Consumer
用来获取 Provider
提供的 value
。虽然可以直接使用 Consumer
来消费 Context
,但 React 更常用 useContext
钩子来获取 Context
的值,写法更加简洁。
jsx
<MyContext.Consumer>
{value => /* 使用 value */}
</MyContext.Consumer>
或者使用更现代的 useContext
:
jsx
const value = useContext(MyContext);
使用步骤
- 创建 Context :用
createContext()
创建一个 Context 对象。 - 提供数据 :使用
Context.Provider
提供数据,通常包裹在应用的顶层。 - 消费数据 :在需要使用数据的地方通过
useContext
获取 这样可以避免层层传递props。以下是一个简单的示例:
jsx
import React from "react";
import { createContext } from "react";
const { Provider, Consumer } = createContext();
function Son() {
return (
<div>
<h4>Son区域</h4>
{/* 在需要接收Grandfather的数据的组件中,添加Consumer */}
<Consumer>
{(value) => (
<h4 style={{ backgroundColor: "lightpink" }}>Son接收的{value}</h4>
)}
</Consumer>
</div>
);
}
function Father() {
return (
<div>
<h3>Father区域</h3>
<Son></Son>
<Consumer>
{(value) => (
<h4 style={{ backgroundColor: "red" }}>Father接收的{value}</h4>
)}
</Consumer>
</div>
);
}
export default function Grandfather() {
const data = "Grandfather的数据";
return (
<div>
<h2>Grandfather</h2>
{/*用Provider进行数据提供,让Grandfather中的后代组件都可以接收*/}
<Provider value={data}>
<Father />
</Provider>
</div>
);
}
小结
- 通过props传值:适合简单场景。
- 使用Context API:适合需要在多个层级组件之间共享数据的情况。
以上就是本文全部内容,希望对你理解React组件传值有所帮助,感谢你的阅读!