从 Vue 的视角学 React(四)—— 组件传参

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

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

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

一、父组件传参到子组件

和 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》

原文地址:https://www.cnblogs.com/wisewrong/p/10948093.html

时间: 2024-10-05 01:17:08

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

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

react父子组件传参 父级向子级传参:在父组件中,我们引入子组件,通过给子组件添加属性,来起到传参的作用,子组件可以通过props获取父组件传过来的参数. 在父组件中: import React from 'react' import ChildCom from './childCom.js' class ParentCom extends React.Component { render() { return ( <div> <h1>父组件</h1> <Chi

vue组件传参

一.父子组件的定义 负值组件的定义有两种,我称为常规父子组件和特殊父子组件. 1.1.常规父子组件 将其他组件以import引入用自定义标签接收,在当前组件中component里注册该标签,页面上可以直接用<自定义标签></自定义标签>样子使用.当前组件为父组件,被引入的组件为子组件. 引入子组件 注册子组件 使用子组件 1.2.特殊父子组件 在路由中定义好组件,组件中含有children,页面上通过<router-view></router-view>形式

前端vue 跨组件传参,cokies,axion

路由跳转 ? 1 2 3 4 5 6 this.$router.push('/course'); this.$router.push({name: course}); this.$router.go(-1); this.$router.go(1); <router-link to="/course">课程页</router-link> <router-link :to="{name: 'course'}">课程页</rout

Vue 子组件向父组件传参

直接上代码 <body> <div id="counter-event-example"> <p>{{ total }}</p> <button-counter v-on:increment="incrementTotal"></button-counter> <button-counter v-on:increment="incrementTotal"><

vue 父子组件传参

父向子组件传参 例子:App.vue为父,引入componetA组件之后,则可以在template中使用标签(注意驼峰写法要改成componet-a写法,因为html对大小写不敏感,componenta与componentA对于它来说是一样的,不好区分,所以使用小写-小写这种写法).而子组件componetA中,声明props参数'msgfromfa'之后,就可以收到父向子组件传的参数了.例子中将msgfromfa显示在<p>标签中.App.vue中 1 1<component-a ms

第八篇:Vue组件传参

组件传参 父传子 1)在子组件内部通过props设置组件的自定义属性 props: ['abc', 'goods'] 2)在父组件渲染子组件是对自定义属性赋值即可 <GoodsBox v-for="goods in goods_list" :abc="goods" :goods="goods"/> 示例代码: 子组件示例代码 <template> <div class="goods-box">

跨组件传参,cookies组件,axios组件

路由跳转 this.$router.push('/course'); this.$router.push({name: course}); this.$router.go(-1); this.$router.go(1); <router-link to="/course">课程页</router-link> <router-link :to="{name: 'course'}">课程页</router-link> 路由

1218 组件分类,组件传参

目录 昨日内容 组件 1.概念 2.组件分类 特点 根组件 局部组件 全局组件 3.组件传参 父传子 子传父 作业 Vue项目环境的搭建 Vue项目环境搭建 Vue项目创建 pycharm配置并启动vue项目 vue项目目录结构分析 vue组件(.vue文件) 全局脚本文件main.js(项目入口) 改写 Vue基础总结 昨日内容 """ 1.表单指令: v-model="变量" 变量与value有关 普通:变量就代表value值 单选框:变量为多个单选框

组件传参

一.两个平行组件传参 新建bus.js import Vue from 'vue' export var bus = new Vue() App.vue里created方法里定义事件 import { bus } from 'bus.js' // ... created () { bus.$on('tip', (text) => { alert(text) }) } Test.vue组件内调用 import { bus } from 'bus.js' // ... bus.$emit('tip'