组件化开发的时候,参数传递是非常关键的环节

哪些参数放在组件内部管理,哪些参数由父组件传入,哪些状态需要反馈给父组件,都需要在设计组件的时候想清楚

但实现这些交互的基础,是明白组件之间参数传递的方式,和各自的优缺点

一、父组件传参到子组件

和 Vue 一样,React 中从父组件到子组件的传参也是通过 props

不过在 Vue 项目中,需要在先组件里定义将要接收的 props,而 React 可以直接获取

而且 props 不光可以接收 Number、String 等基本类型,还可以接收 Function,这点在《从 Vue 的视角学 React(三)—— 事件处理》中也有提到

另外 props 还可以直接接收组件,类似于 Vue 中的 slot,后面会详细介绍

二、子组件向父组件传参

在项目中,也会有需要更新 props 的时候

但和 Vue 一样,React 中的 props 也是单向的,一定不能在组件内部直接修改 props 的值

而应该采用事件上报的方式,让父组件修改相应的状态

整个过程就像是 Vue 中的 $emit,只是将 $emit 中声明的自定义事件放到了 props 中

上面的 updateText 就是一个自定义事件,父组件为这个自定义事件添加了处理函数 handleUpdateText

如果子组件需要向父组件传递参数,可以在触发自定义事件 updateText 的时候,向事件处理函数传参

然后父组件在对应的事件处理函数 handleUpdateText 中获取到子组件传过来的参数

三、状态提升

除了父子之间的传参,还会有两个平级组件之间的参数传递

React 的推荐方案是,将两个子组件的参数都放到父组件中处理,这就是状态提升

假设有 boy 和 girl 两个组件,它们都是组件 father 的子组件

组件 girl 中有一个 food 状态,组件 boy 中有一个 age 状态,food 会随着 age 的变化而变化

如果 age 和 food 都是对应组件的 state,维护起来就比较麻烦

而如果将 age 和 food 都作为 props 传入,就可以在父组件 father 中维护这两个状态

当需要更新 age 的时候,从 boy 组件上报事件,然后在父组件中同时更新 food 和 age

React 是单向数据流,在设计组件的时候,应当始终保持自上而下的数据传递,而不是尝试在不同组件间同步 state

如果某些数据可以由 props 或 state 推导得出,那么它就不应该存在于 state 中

虽然这种方式会比双向绑定需要编写更多的代码,但更利于维护

四、限制 props 类型

良好的组件不可避免的会用到很多 props,为这些 props 添加类型校验就尤为重要

Vue 本身就有一套很完善的 props 类型校验配置,React 之前也有 React.PropTypes

但支持 TypeScript 之后,就将这部分功能拆成了独立的第三方库 prop-types

npm install prop-types -S

引入 PropTypes 之后,在组件内定义一个静态属性 propTypes(注意大小写),然后定义具体的规则

参考上图的示例,校验规则由  .  语法连接,如果有多个类型,就使用 oneOfType

更多关于 PropTypes 的使用可以查看官方文档

除了类型校验,有时候还需要给 props 添加默认值,这时候可以用 defaultProps

五、组件嵌套

虽然开发组件的时候,我们总是希望能尽量满足需求,以减少后期的工作量

但一万个读者就有一万个哈姆雷特,一个组件不可能满足所有用户的需求,所以有时候就需要为组件提供一些扩展性

另外,还有一些组件本身就是作为容器开发的,这些场景都需要将组件作为 props 传给子组件

const Child = props => <div>{props.content}</div> 

const Tag = props => <div>This is tag</div>

const Parents = props => <Child content={<Tag />}/>

上面的代码中,子组件 Child 会接收一个 props 属性 content

然后在父组件 Parents 中,将 Tag 组件作为 content 的值传给了子组件 Child

不过这样的写法并不优雅,如果能像 Vue 的 slot 那样,直接将子组件嵌套在父组件的标签内,就更符合 HTML 标签的结构

这时候就可以用到 props.children

const Child = props => <div>{props.children}</div> 

const Tag = props => <div>This is tag</div>

const Parents = props => <Child><Tag /></Child>

从上面的代码可以看出,组件标签之间嵌套的内容,可以在组件内通过 props.children 接收到

事实上,props.children 是一个数组,如果不加具体的元素下标,就会将所有的元素渲染出来

如果标签内有多个节点,porps.children 就会将自身组件作为根节点,以数组的形式将组件内的 DOM 结构虚拟出来

而且 props.children 不单可以接收组件,还可以接收字符串

六、Context

在维护大型项目的时候,仅靠 props 和事件来传参是不够的,所以 Vue 提供了 Vuex 来维护状态

React 也有状态管理工具,常用的有 ReduxMobx,如何使用这些工具我会在以后的文章中介绍

但在使用这些工具之前,需要了解 context,因为 Redux 和 Mobx 都是基于 context 实现的

如果组件的层级很深,仅使用 props 层层嵌套传参的话就非常的冗余

这时如果有一个全局变量,在顶层组件定义之后,直接在底层组件中获取,就会非常简洁,context 就是这样的全局变量

// Context 可以让我们无须明确地传遍每一个组件,就能将值深入传递进组件树。
// 为当前的 theme 创建一个 context(“light”为默认值)。
const ThemeContext = React.createContext('light'); class App extends React.Component {
render() {
// 使用一个 Provider 来将当前的 theme 传递给以下的组件树。
// 无论多深,任何组件都能读取这个值。
// 在这个例子中,我们将 “dark” 作为当前的值传递下去。
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
} // 中间的组件再也不必指明往下传递 theme 了。
function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
} class ThemedButton extends React.Component {
// 指定 contextType 读取当前的 theme context。
// React 会往上找到最近的 theme Provider,然后使用它的值。
// 在这个例子中,当前的 theme 值为 “dark”。
static contextType = ThemeContext;
render() {
return <Button theme={this.context} />;
}
}

但 context 的使用会极大的增强组件之间的耦合性,项目中并不建议直接使用

所以我直接复制粘贴了官方文档的代码,仅为了解 context 这个概念

小型项目中,如果有深层次的传参,应当从组件设计上解决问题,比如直接将组件传下去

而大型项目中,如果需要用到 context,更推荐使用 redux 和 mobx 这些成熟的状态管理工具

参考资料:

《React 状态提升》

《React.js 的 context》

从 Vue 的视角学 React(四)—— 组件传参的更多相关文章

  1. React(7) --react父子组件传参

    react父子组件传参 父级向子级传参:在父组件中,我们引入子组件,通过给子组件添加属性,来起到传参的作用,子组件可以通过props获取父组件传过来的参数. 在父组件中: import React f ...

  2. 从 Vue 的视角学 React(三)—— 事件处理

    如果要处理某个元素的 click 事件,原生 js 可以直接为该元素添加一个 onclick 函数 Vue 封装了 v-on 指令,可以简化为 @click 并添加相应的函数 React 的开发思想是 ...

  3. 从 Vue 的视角学 React(二)—— 基本语法

    基于 Vue.js 开发的时候,每个 vue 文件都是一个单独的组件,可以包含 HTML,JS,CSS 而 React 是以函数为基础,每个 function 就是一个组件.虽然 JSX 让 HTML ...

  4. 从 Vue 的视角学 React(一)—— 项目搭建

    虽然 Vue 在国内一家独大,但在全球范围内,React 依然是最流行的前端框架 最近和一些朋友聊天,发现很多项目都选择了 React 技术栈,而且公司的新项目也决定使用 React 我一直以来都是走 ...

  5. vue组件传参

    一.父子组件的定义 负值组件的定义有两种,我称为常规父子组件和特殊父子组件. 1.1.常规父子组件 将其他组件以import引入用自定义标签接收,在当前组件中component里注册该标签,页面上可以 ...

  6. vue中的路由传参及跨组件传参

    路由跳转   this.$router.push('/course'); this.$router.push({name: course}); this.$router.go(-1); this.$r ...

  7. vue 组件传参及跨域传参

    可以完成跨组件传参的四种方式 // 1) localStorage:永久存储数据 // 2) sessionStorage:临时存储数据(刷新页面数据不重置,关闭再重新开启标签页数据重置) // 3) ...

  8. Vue 子组件向父组件传参

    直接上代码 <body> <div id="counter-event-example"> <p>{{ total }}</p> & ...

  9. vue 父子组件传参

    父向子组件传参 例子:App.vue为父,引入componetA组件之后,则可以在template中使用标签(注意驼峰写法要改成componet-a写法,因为html对大小写不敏感,component ...

随机推荐

  1. JSOI 2010 连通数

    洛谷 P4306 [JSOI2010]连通数 洛谷传送门 题目描述 度量一个有向图联通情况的一个指标是连通数,指图中可达顶点对个的个数. 如图 顶点 11 可达 1,~2,~3,~4,~51, 2, ...

  2. JDOJ 2157 Increasing

    洛谷 P3902 递增 洛谷传送门 JDOJ 2157: Increasing JDOJ传送门 Description 数列A1,A2,--,AN,修改最少的数字,使得数列严格单调递增. Input ...

  3. hadoop java.io.EOFException: Unexpected end of input stream

    执行hadoop 报错 java.io.EOFException: Unexpected end of input stream at org.apache.hadoop.io.compress.De ...

  4. yii2 Query Builder 查询打印sql语句

    $query = new Query(); $query->select('gs.*, g.goods_images, sa.attr_name, sa.is_default, sa.alias ...

  5. vue大文件上传控件选哪个好?

    需求: 项目要支持大文件上传功能,经过讨论,初步将文件上传大小控制在20G内,因此自己需要在项目中进行文件上传部分的调整和配置,自己将大小都以20G来进行限制. PC端全平台支持,要求支持Window ...

  6. RE:SB的SDOISB记

    Day0 到了农大 进门看见hly 和myj一起乱%一通 一本爷的气场就是强大 晚上gryz搬砖三人组出去吃饭,开心>_< 吃完饭后去试机 手速比较快,写了一下ntt,lct,sa和一些小 ...

  7. 【CSP-S膜你考】我们的可可西里

    我们的可可西里 题面 转眼到了2008年的6月9日,盼望已久的高考结束了.我们踏上了向西的旅程(本来是想写西去之路,可是考虑不太妥当).可可西里,多么诱人的名词,充满了奇幻的色彩和自然的淳朴.从可可西 ...

  8. web服务本质

    目录 django 框架引入: web框架本质 HTTP协议 多功能web服务 封装,分发处理 django 框架引入: web框架本质 web框架本质: 软件开发架构: c / s ; b/ s - ...

  9. 第08组 Beta冲刺(4/5)

    队名:955 组长博客:点这里! 作业博客:点这里! 组员情况 组员1(组长):庄锡荣 过去两天完成了哪些任务 文字/口头描述 ? 测试新功能中 展示GitHub当日代码/文档签入记录 接下来的计划 ...

  10. Java编程思想之十一 持有对象

    如果一个程序只包含固定数量的且其生命期都是已知的对象,那么这是一个非常简单的程序. 11.1 泛型和类型安全的容器 使用ArrayList:创建一个实例,用add()插入对象,然后用get()访问对象 ...