大型情景剧之React组件传值

引言:

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,并使用解构赋值从中提取出nameage
  • 这些值被用来在子组件的JSX中进行渲染。

注意:

我们要注意父组件给子组件传值中,是通过props进行传递的,People组件在使用nameage时没有正确地从props中解构出来。运行会报错,因为nameage变量在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的状态,背景颜色和按钮文本会发生变化。
    • 如果scorefalse,背景颜色为红色,按钮文本为"没发挥好"。
    • 如果scoretrue,背景颜色为蓝色,按钮文本为"拿了奖状!"。
2. 父组件 Parent
  • 函数组件Parent是一个函数组件。
  • 回调函数 :定义了一个名为comment的函数,它根据传入的status参数打印不同的消息到控制台。
    • 如果statustrue,打印"小宝真棒!"。
    • 如果statusfalse,打印"小宝辛苦了"。
  • 渲染子组件Parent组件渲染了一个Child组件,并将comment函数作为childMood属性传递给Child组件。
父子传值过程
  1. 父组件创建子组件实例

    • Parent组件中,我们创建了一个Child组件实例,并将comment函数作为childMood属性传递给Child组件。
  2. 传递属性

    • Parent组件通过<Child childMood={comment} />comment函数传递给Child组件。
  3. 子组件接收并使用属性

    • Child组件通过函数参数接收props对象,并从中解构出childMood
    • 当用户点击按钮时,handleClick函数被调用,切换score状态,并调用childMood函数,将新的score状态传递给父组件。
  4. 父组件响应子组件的事件

    • Parent组件中的comment函数接收到子组件传递的score状态,并根据这个状态打印相应的消息到控制台。

情景剧版解释(纯个人理解,理解有误请指出)

第一幕:小宝回家
  • 小宝 (子组件 Child):小宝带着考试的心情回到了家。他有一个初始的心情状态(score),表示他是否考得好。
  • 父母 (父组件 Parent):父母在家里等待小宝回来,并准备根据小宝的成绩给予反馈。
第二幕:小宝汇报成绩
  • 小宝:小宝按下按钮向父母汇报自己的成绩。

    • 如果成绩不好(scorefalse),按钮显示"没发挥好"。
    • 如果成绩好(scoretrue),按钮显示"拿了奖状!"。
  • 点击按钮

    • 小宝点击按钮,触发handleClick函数。
    • handleClick函数会切换score的状态,并调用childMood函数将新的score状态传递给父母。
  • 背景颜色变化

    • 如果scorefalse,背景颜色为红色。
    • 如果scoretrue,背景颜色为蓝色。
第三幕:父母的反馈
  • 父母 :父母接收到小宝的汇报(score状态),并通过comment函数给出反馈。
    • 如果scoretrue,父母在控制台打印"小宝真棒!"。
    • 如果scorefalse,父母在控制台打印"小宝辛苦了"。
详细步骤
  1. 初始状态

    • 小宝回家时,score初始值为false,表示没发挥好。
    • 背景颜色为红色,按钮文本为"没发挥好"。
  2. 第一次汇报

    • 小宝点击"没发挥好"按钮。
    • handleClick函数被调用,scorefalse变为true
    • childMood函数被调用,传递新的score状态true给父母。
    • 父母接收到scoretrue,在控制台打印"小宝真棒!"。
    • 背景颜色变为蓝色,按钮文本变为"拿了奖状!"。
  3. 第二次汇报

    • 小宝再次点击"拿了奖状!"按钮。
    • handleClick函数被调用,scoretrue变为false
    • childMood函数被调用,传递新的score状态false给父母。
    • 父母接收到scorefalse,在控制台打印"小宝辛苦了"。
    • 背景颜色变为红色,按钮文本变为"没发挥好"。
小结
  • 小宝 (子组件 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状态,并显示"小宝考了一百分!"。

详细步骤

  1. 初始状态

    • Parent组件中的message状态初始为空。
    • Child1组件显示空消息。
    • Child2组件显示按钮"告诉哥哥"。
  2. 点击按钮

    • 用户点击Child2中的按钮"告诉哥哥"。
    • Child2调用tellParent函数,并传递score("小宝考了一百分!")给Parent
  3. 更新状态

    • Parent接收到score,并通过getData函数更新message状态为"小宝考了一百分!"。
  4. 显示消息

    • 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);

使用步骤

  1. 创建 Context :用 createContext() 创建一个 Context 对象。
  2. 提供数据 :使用 Context.Provider 提供数据,通常包裹在应用的顶层。
  3. 消费数据 :在需要使用数据的地方通过 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组件传值有所帮助,感谢你的阅读!

相关推荐
Justinc.5 分钟前
CSS3新增边框属性(五)
前端·css·css3
fruge12 分钟前
纯css制作声波扩散动画、js+css3波纹催眠动画特效、【css3动画】圆波扩散效果、雷达光波效果完整代码
javascript·css·css3
neter.asia21 分钟前
vue中如何关闭eslint检测?
前端·javascript·vue.js
~甲壳虫21 分钟前
说说webpack中常见的Plugin?解决了什么问题?
前端·webpack·node.js
光影少年40 分钟前
vue2与vue3的全局通信插件,如何实现自定义的插件
前端·javascript·vue.js
As977_42 分钟前
前端学习Day12 CSS盒子的定位(相对定位篇“附练习”)
前端·css·学习
susu108301891144 分钟前
vue3 css的样式如果background没有,如何覆盖有background的样式
前端·css
Ocean☾1 小时前
前端基础-html-注册界面
前端·算法·html
Rattenking1 小时前
React 源码学习01 ---- React.Children.map 的实现与应用
javascript·学习·react.js
Dragon Wu1 小时前
前端 Canvas 绘画 总结
前端