React Props 详解在组件之间单向传递数据与构建可复用组件【免费下载链接】curriculumThe open curriculum for learning web development项目地址: https://gitcode.com/GitHub_Trending/cu/curriculum导读本篇文章是仓库react/getting_started_with_react/passing_data_between_components.md课程的系统化展开围绕 React 中组件间数据传递的核心机制——propsproperties 的缩写展开。你将理解数据如何从父组件流向子组件为什么 props 能消灭大量重复代码如何用解构、默认值、函数 props 写出简洁且可定制的组件。读完本文你可以独立用 props 搭建可复用、可配置的组件树并能在实战项目中正确区分 props 与 state 的职责。数据如何在 React 组件间流动React 的数据流有一个非常重要的规则数据通过 props 从父组件传递到子组件并且是单向流动unidirectional的。// 父组件 App 把数据交给子组件 Child function Child(props) { return p{props.message}/p; } function App() { return Child message来自父组件的数据 /; }单向数据流意味着数据只能从上父往下子流动对 props 数据的任何修改只会影响使用该数据的子组件而不会反向影响父组件或兄弟组件父组件对子组件拥有显式控制权这使数据流向可预测、易于追踪从而显著减少应用中的错误。这种约束看似限制实则是 React 设计哲学的核心数据流向越明确状态越容易推理。关于组件如何定义、如何通过export/import让父子组件建立关系可参考 React 组件基础课程而组件最终渲染出的 JSX 本质上只是createElement的语法糖会编译成普通 JavaScript 对象props 正是挂在这些元素对象上的数据。为什么需要 props复用组件的困境想象一个会被多次渲染的Button组件function Button() { return buttonClick Me!/button; } export default function App() { return ( div Button / Button / Button / /div ); }组件复用的效果很好但有一个问题如果想让第二个按钮的文字变成 Dont Click Me!当前唯一办法是再写一个Button2function Button() { return buttonClick Me!/button; } function Button2() { return buttonDont Click Me!/button; } export default function App() { return ( div Button / Button2 / Button / /div ); }如果只有两三个按钮这还能接受。但假设有 10 个按钮且每个按钮的文字、字体、颜色、尺寸各不相同——为每一种组合创建一个新组件会迅速累积成大量的代码重复。这正是 props 要解决的问题用一个组件通过传入不同的 props 值覆盖任何数量的变体。使用 props 传递数据React 的函数式组件本质是接收 props 的 JavaScript 函数。把Button改造为接收 props 的版本function Button(props) { const buttonStyle { color: props.color, fontSize: props.fontSize px }; return button style{buttonStyle}{props.text}/button; } export default function App() { return ( div Button textClick Me! colorblue fontSize{12} / Button textDont Click Me! colorred fontSize{12} / Button textClick Me! colorblue fontSize{20} / /div ); }这段代码里有三个关键点组件接收props作为函数参数Button(props)组件内部通过props.propertyName引用各个属性渲染时在组件上定义 prop 值text、color的取值是字符串用双引号直接书写fontSize是数字表达式必须放在花括号{12}中。JSX 中所有动态值都必须用花括号包裹这正是 JSX 课程所强调的在标记中嵌入 JavaScript的用法内联样式是动态生成的buttonStyle对象由 props 计算得出再通过style{buttonStyle}应用到button上。注意 JSX 属性名的差异HTML 中的font-size在 JSX 中要写成 camelCase 的fontSizeclass要写成className因为 JSX 属性最终会成为 JavaScript 对象的键。由于 CSS 单位不能省略这里用props.fontSize px拼接出12px、20px这样的样式值。Prop 解构更简洁的组件参数在 React 代码中你会频繁见到**解构赋值destructuring**模式直接在函数参数中把 props 解包出来。这能让代码更简洁、可读性更强function Button({ text, color, fontSize }) { const buttonStyle { color: color, fontSize: fontSize px }; return button style{buttonStyle}{text}/button; } export default function App() { return ( div Button textClick Me! colorblue fontSize{12} / Button textDont Click Me! colorred fontSize{12} / Button textClick Me! colorblue fontSize{20} / /div ); }对比前一个版本{ text, color, fontSize }直接声明组件依赖哪些 props后续引用时无需再写props.前缀。这是现代函数式组件的主流写法也便于配合下一节的默认值语法。默认 props消除重复与 undefined观察上面的App多个Button重复传入了相同的textClick Me!、colorblue、fontSize{12}。为了不再重复定义这些公共值同时防止组件收到undefined导致渲染异常可以在函数参数上定义默认值function Button({ text Click Me!, color blue, fontSize 12 }) { const buttonStyle { color: color, fontSize: fontSize px }; return button style{buttonStyle}{text}/button; } export default function App() { return ( div Button / Button textDont Click Me! colorred / Button fontSize{20} / /div ); }现在只有当某个 prop 的值与默认值不同时才需要显式传入第一个Button /三个 prop 全部使用默认值第二个只覆盖text和colorfontSize沿用默认的 12第三个只覆盖fontSize为 20其余保持默认。defaultProps老代码库中的传统方案在部分既有代码库中你还会看到defaultProps的用法。它曾是设置 props 默认值的传统方式尤其在 class 组件中非常普遍function Button({ text, color, fontSize }) { const buttonStyle { color: color, fontSize: fontSize px }; return button style{buttonStyle}{text}/button; } Button.defaultProps { text: Click Me!, color: blue, fontSize: 12 }; export default function App() { return ( div Button / Button textDont Click Me! colorred / Button fontSize{20} / /div ); }需要明确的是React 已不再为函数组件接受defaultProps这种写法函数组件请使用参数默认值语法。但理解defaultProps仍然有价值——当你维护 class 组件或历史代码库时依然会遇到它。关于 class 组件中 props 的完整用法constructor(props)、super(props)、通过this.props访问可阅读仓库中的 class 组件课程。函数作为 props把行为传给子组件props 不仅能传递变量还能传递函数。这为组件提供了行为注入的能力function Button({ text Click Me!, color blue, fontSize 12, handleClick }) { const buttonStyle { color: color, fontSize: fontSize px }; return ( button onClick{handleClick} style{buttonStyle} {text} /button ); } export default function App() { const handleButtonClick () { window.location.href https://www.google.com; }; return ( div Button handleClick{handleButtonClick} / /div ); }这里发生了三件事handleButtonClick定义在父组件App中它的引用作为handleClickprop 的值传给Button子组件Button接收到该函数并将其绑定到button的onClick事件上点击时被调用。传递引用而非调用结果一个关键细节我们只传函数引用绝不能加括号。handleClick{handleButtonClick}是传引用如果写成handleClick{handleButtonClick()}函数会在按钮渲染的瞬间就被执行——这几乎总是 bug 的来源。给函数传参数匿名函数包装上面的写法有一个局限所有使用该 prop 的Button点击后都会跳到同一个页面。如果需要让不同按钮跳转到不同地址可以把参数也通过 props 传入function Button({ text Click Me!, color blue, fontSize 12, handleClick }) { const buttonStyle { color: color, fontSize: fontSize px }; return ( button onClick{handleClick} style{buttonStyle} {text} /button ); } export default function App() { const handleButtonClick (url) { window.location.href url; }; return ( div Button handleClick{() handleButtonClick(https://www.google.com)} / /div ); }注意这里不能直接写onClick{handleClick(https://...)}原因同上——那会在渲染期间立即调用函数。正确做法是传一个匿名函数的引用由它在事件触发时再去调用带参函数handleClick{() handleButtonClick(https://...)}。作为一种替代实现也可以使用柯里化函数curried functions让handleClick返回一个新函数从而在渲染时安全地预绑定参数感兴趣可以自行查阅函数式编程中柯里化的相关知识。仓库源码中的 props 实践印证props 并非孤立概念仓库中多个课程都在实际使用它可以作为学习佐证向列表组件传递数据在 JSX 渲染技巧课程中List组件接收animals数组 prop再通过map把每个元素作为animalprop 传给ListItemfunction ListItem(props) { return li{props.animal}/li; } function List(props) { return ( ul {props.animals.map((animal) { return ListItem key{animal} animal{animal} /; })} /ul ); } function App() { const animals [Lion, Cow, Snake, Lizard]; return ( div h1Animals: /h1 List animals{animals} / /div ); }这里还出现了一个 props 家族的特殊成员key。key是 React 内部用于区分列表项实例的特殊 prop它不会通过props参数传递给组件。关于 key 的来源应优先使用数据自带的唯一 id而非数组下标与 anti-pattern可阅读 React keys 课程。ESLint 中的 props 校验提示如果你在编辑器中看到 props 下方出现波浪线提示missing in props validation这来自默认的 ESLint 规则react/prop-types它属于类型校验层面的建议后续课程会专门介绍 PropTypes 等方案。若暂时不想看到该提示可在eslint.config.js中关闭rules: { // Your other rules react/prop-types: off }这一实践同样记录在 渲染技巧课程中。实战项目CV 应用在本课程的实战项目 CV Application 中你将构建一个用户输入信息并生成简历的应用任务明确要求make heavy use of state and props——把整个界面拆分为通用信息区教育经历区工作经验区等独立组件通过 props 在各组件间传递表单数据并通过 props 传递编辑/提交等回调函数。这是把本课所有知识点落地的理想练习。知识检查回顾以下问题检验你是否掌握了本课核心React 组件之间的数据如何流动从子到父从父到子还是双向——答案单向从父组件通过 props 流向子组件为什么要在 React 中使用 props——为了消除重复组件代码用一个组件通过不同 props 值覆盖任意数量的变体实现真正的可复用与可定制如何在 React 组件上定义默认属性这样做有哪些好处——函数组件用参数默认值{ text Click Me! }class/老代码库用defaultProps好处是减少重复传值、防止undefined导致的渲染异常如何把函数作为 props 传递——传函数引用不加括号需要参数时用匿名函数包装避免渲染期间被调用。作为下一步理解 props 之后你还可以继续学习如何向子组件传递children、如何在组件树中共享状态以及组件间更深层的数据交互模式——props 是这一切的基础。【免费下载链接】curriculumThe open curriculum for learning web development项目地址: https://gitcode.com/GitHub_Trending/cu/curriculum创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考