(四)从零学 React Props:数据传递 + 实战案例 + 避坑指南

一、为什么需要Props?------ 解决组件"定制化"问题

上节课我们学会了创建和使用组件,但有个明显的问题:组件内容是固定的。比如创建的UserCard组件,无论用多少次,都只能显示"李四"的信息;Button组件也只能显示"点击我"。

但实际开发中,组件需要根据不同场景展示不同内容:

  • 按钮可能需要显示"登录"、"注册"、"提交"等不同文字;
  • 用户卡片需要展示"张三"、"李四"等不同用户的信息;
  • 商品卡片需要展示不同商品的名称、价格、图片。

这就需要一种方式让父组件给子组件传递数据 ,而Props(全称Properties,属性)就是React专门用来解决这个问题的机制。

形象理解 :Props就像组件的"参数"。比如你去咖啡店点咖啡(调用咖啡组件),需要告诉店员"要拿铁"(传递type="latte")、"少糖"(传递sugar="less"),店员根据这些参数制作出你要的咖啡(组件根据Props展示内容)。

二、Props的基本使用:传递与接收

Props的使用分为两步:父组件传递数据子组件接收并使用数据

1. 父组件传递Props:像写HTML属性一样简单

父组件在使用子组件时,通过"属性=值"的形式传递数据,和HTML标签的属性写法类似。

例如,父组件App要给子组件Button传递"按钮文字"和"颜色":

jsx 复制代码
// 父组件 App.js
import Button from './Button';

function App() {
  return (
    <div>
      {/* 传递Props:text是属性名,"登录"是属性值 */}
      <Button text="登录" color="blue" />
      
      {/* 再传一组不同的Props */}
      <Button text="注册" color="green" />
    </div>
  );
}

export default App;
传递数据的类型:

Props可以传递任意类型的数据,包括:

  • 字符串(直接写,如text="登录");
  • 数字(需要用{}包裹,如count={5});
  • 布尔值(用{},如disabled={true});
  • 数组(用{},如list={[1,2,3]});
  • 对象(用{},如user={``{name: "张三", age: 20}})。
jsx 复制代码
// 传递多种类型的Props
<Button 
  text="购买" 
  price={99}  // 数字
  isNew={true}  // 布尔值
  tags={["热销", "新品"]}  // 数组
/>

2. 子组件接收Props:函数参数就是Props对象

函数组件通过函数的第一个参数接收父组件传递的所有Props,这个参数是一个对象,属性名就是父组件传递的属性名。

例如,Button组件接收并使用Props:

jsx 复制代码
// 子组件 Button.js
// props是一个对象,包含父组件传递的所有属性(text、color等)
function Button(props) {
  console.log(props); // 打印看看Props内容:{ text: "登录", color: "blue" }(第一次渲染时)
  
  return (
    <button 
      style={{ 
        backgroundColor: props.color,  // 使用color属性
        color: "white", 
        padding: "8px 16px",
        border: "none",
        borderRadius: "4px",
        margin: "0 8px"
      }}
    >
      {props.text}  {/* 使用text属性 */}
    </button>
  );
}

export default Button;

运行后,页面会显示两个按钮:

  • 蓝色背景的"登录"按钮;
  • 绿色背景的"注册"按钮。
    这就是Props的作用:同一个组件,通过不同的Props展示不同内容。

3. 简化Props接收:解构赋值

如果Props属性很多,每次写props.textprops.color会很繁琐。可以用对象解构直接提取需要的属性,让代码更简洁。

修改Button组件:

jsx 复制代码
// 用解构赋值直接提取text和color
function Button({ text, color }) {
  return (
    <button 
      style={{ 
        backgroundColor: color,  // 直接用color
        // 其他样式...
      }}
    >
      {text}  {/* 直接用text */}
    </button>
  );
}

效果和之前完全一样,但代码更简洁。这是React开发中最常用的写法,一定要掌握。

三、Props的核心特性:只读性(不可修改!)

这是React的重要原则:子组件不能修改接收到的Props,Props是"只读的"(read-only)。

为什么Props不能修改?

  • Props的数据来源是父组件,修改Props会导致数据流向混乱(父组件和子组件都可能改数据,难以追踪);
  • React通过"单向数据流"(父→子)保证数据可预测性,方便调试和维护。

错误示例:尝试修改Props

jsx 复制代码
function Button({ text }) {
  // 尝试修改Props(会报错!)
  text = "新文字";  // 错误:Props是只读的,不能重新赋值
  
  return <button>{text}</button>;
}

运行后控制台会报错,提示"Assignment to constant variable"(给常量赋值),因为Props被视为不可变数据。

正确做法:如果需要修改,用子组件自己的状态

如果子组件需要修改数据,应该把数据存在子组件的"状态"(State)中,这部分我们下节课会详细讲。Props只负责"接收外部数据",不负责"存储内部可变数据"。

四、Props默认值:给组件设置"默认参数"

如果父组件使用子组件时没有传递某个Props,子组件可以设置默认值,避免出现undefined

设置默认值有两种方式:

1. 函数参数默认值(推荐,简单直观)

直接在解构赋值时给属性设置默认值:

jsx 复制代码
// 给color设置默认值"gray",text设置默认值"按钮"
function Button({ text = "按钮", color = "gray" }) {
  return (
    <button style={{ backgroundColor: color }}>
      {text}
    </button>
  );
}

当父组件不传递Props时,会使用默认值:

jsx 复制代码
// 父组件中不传递text和color
<Button />  // 会显示灰色背景的"按钮"

2. 组件的defaultProps属性(了解即可)

这是另一种设置默认值的方式,通过组件的defaultProps属性:

jsx 复制代码
function Button({ text, color }) {
  return (
    <button style={{ backgroundColor: color }}>
      {text}
    </button>
  );
}

// 设置默认Props
Button.defaultProps = {
  text: "按钮",
  color: "gray"
};

效果和函数参数默认值一样,但现在更推荐用第一种方式(更简洁,符合ES6语法)。

五、Props类型检查:让组件更健壮(基础版)

大型项目中,为了避免传递错误类型的Props(比如应该传数字却传了字符串),可以给Props做类型检查。React推荐用prop-types库来实现。

步骤1:安装prop-types

在终端中进入项目目录,执行安装命令:

bash 复制代码
npm install prop-types --save

步骤2:在组件中使用类型检查

Button组件为例,限制text是字符串,color是字符串,price是数字:

jsx 复制代码
import PropTypes from 'prop-types';  // 导入PropTypes

function Button({ text, color, price }) {
  return (
    <button style={{ backgroundColor: color }}>
      {text} {price && `¥${price}`}
    </button>
  );
}

// 定义Props类型检查规则
Button.propTypes = {
  text: PropTypes.string,  // text必须是字符串
  color: PropTypes.string, // color必须是字符串
  price: PropTypes.number  // price必须是数字
};

export default Button;

如果父组件传递错误类型(比如price="99",字符串),控制台会警告:
Warning: Failed prop type: Invalid prop 'price' of type 'string' supplied to 'Button', expected 'number'.

常用类型检查规则:

  • PropTypes.string:字符串
  • PropTypes.number:数字
  • PropTypes.bool:布尔值
  • PropTypes.array:数组
  • PropTypes.object:对象
  • PropTypes.func:函数
  • PropTypes.node:可以渲染的内容(数字、字符串、元素等)
  • PropTypes.isRequired:必须传递的属性(在类型后加,如PropTypes.string.isRequired
jsx 复制代码
Button.propTypes = {
  text: PropTypes.string.isRequired,  // text必须传递,且是字符串
  price: PropTypes.number  // price可选,但如果传必须是数字
};

如果父组件没传递text(加了isRequired),会警告:Warning: Failed prop type: The prop 'text' is marked as required in 'Button', but its value is 'undefined'.

六、综合案例:用户列表组件(复用+Props传递)

我们来创建一个UserList父组件,传递多个用户数据给UserCard子组件,展示用户列表。

步骤1:改造UserCard组件(接收Props)

jsx 复制代码
// src/UserCard.js
import PropTypes from 'prop-types';

// 接收用户数据作为Props
function UserCard({ user }) {
  return (
    <div style={{ 
      border: '1px solid #ddd', 
      padding: '16px', 
      borderRadius: '8px', 
      margin: '8px',
      maxWidth: '200px'
    }}>
      <img 
        src={user.avatar} 
        alt={user.name} 
        style={{ width: '80px', borderRadius: '50%' }}
      />
      <h3>{user.name}</h3>
      <p>{user.age}岁 | {user.gender}</p>
      <p>{user.bio}</p>
    </div>
  );
}

// 类型检查
UserCard.propTypes = {
  user: PropTypes.shape({  // 检查对象内部属性
    name: PropTypes.string.isRequired,
    avatar: PropTypes.string.isRequired,
    age: PropTypes.number.isRequired,
    gender: PropTypes.string,
    bio: PropTypes.string
  }).isRequired  // user必须传递
};

export default UserCard;

步骤2:创建UserList组件(传递Props)

jsx 复制代码
// src/UserList.js
import UserCard from './UserCard';

function UserList() {
  // 模拟用户数据(实际项目可能来自接口)
  const users = [
    {
      name: "张三",
      avatar: "https://picsum.photos/id/1/200",
      age: 25,
      gender: "男",
      bio: "前端工程师"
    },
    {
      name: "李四",
      avatar: "https://picsum.photos/id/2/200",
      age: 23,
      gender: "女",
      bio: "UI设计师"
    },
    {
      name: "王五",
      avatar: "https://picsum.photos/id/3/200",
      age: 28,
      gender: "男",
      bio: "产品经理"
    }
  ];

  return (
    <div style={{ display: 'flex', flexWrap: 'wrap' }}>
      {/* 遍历用户数据,给每个UserCard传递不同的user */}
      {users.map((user, index) => (
        <UserCard key={index} user={user} />  {/* 传递user对象作为Props */}
      ))}
    </div>
  );
}

export default UserList;

步骤3:在App中使用UserList

jsx 复制代码
// src/App.js
import UserList from './UserList';

function App() {
  return (
    <div style={{ padding: '20px' }}>
      <h1>用户列表</h1>
      <UserList />
    </div>
  );
}

export default App;

运行效果:

页面会显示3个用户卡片,每个卡片展示不同用户的信息(姓名、头像、年龄等)。通过Props,我们实现了UserCard组件的复用,只写一次组件,就能展示不同数据。

七、常见问题与解决办法

  1. Props传递后显示undefined

    可能原因:

    • 父组件传递的属性名和子组件接收的不一致(比如父传username,子取name);
    • 子组件没正确解构Props(比如写成{ userName }但实际属性是userName)。
      解决:检查属性名拼写,用console.log(props)在子组件中打印Props,确认数据是否传递成功。
  2. 传递数字/布尔值时显示错误

    错误写法:<Button count="5" />(会被当作字符串"5")

    解决:用{}包裹非字符串类型:<Button count={5} isActive={true} />

  3. 忘记安装prop-types导致报错

    报错:Cannot find module 'prop-types'

    解决:在终端执行npm install prop-types --save安装依赖。

相关推荐
qq7798233404 小时前
React组件完全指南
前端·javascript·react.js
EndingCoder4 小时前
MongoDB基础与Mongoose ODM
服务器·javascript·数据库·mongodb·中间件·node.js
qq7798233404 小时前
React Hooks完全指南
前端·javascript·react.js
Moment4 小时前
性能狂飙!Next.js 16 重磅发布:Turbopack 稳定、编译提速 10 倍!🚀🚀🚀
前端·javascript·后端
软件技术NINI4 小时前
html css js网页制作成品——HTML+CSS仙台有树电视剧网页设计(5页)附源码
javascript·css·html
DoraBigHead5 小时前
React Fiber:从“递归地狱”到“时间切片”的重生之路
前端·javascript·react.js
YUELEI1185 小时前
Vue 安装依赖的集合和小知识
javascript·vue.js·ecmascript
lecepin6 小时前
AI Coding 资讯 2025-10-22
前端·javascript·后端
gustt6 小时前
深入理解 JavaScript 的对象与代理模式(Proxy)
javascript