组件是 React 的核心!了解组件和组件间的值传递 Props

404 阅读8分钟

组件的概念及应用

组件允许你将 UI 拆分为独立可复用的代码片段,并对每个片段进行独立构思。从概念上类似于 JavaScript 函数。它接受任意的入参(即 “props”),并返回用于描述页面展示内容的 React 元素,分为函数组件与 class 类组件。

学习组件之前,可以先安装一个 VS Code  插件,直接搜索 react , 选择下载量最高的就行了。

函数组件与类组件

顾名思义,函数组件就是使用函数创建的组件,类组件就是使用类创建组件,安装好插件之后,可以直接使用简写补全功能创建对应插件。

注意,首字母要大写

函数组件的创建

编辑器快捷方式:rfce

import React from 'react'

function ComponentFun() {
  return (
    <div>
      <h2>单文件函数组件</h2>
    </div>
  )
}

export default ComponentFun

声明函数,并在函数中返回 JSX ,最后导出函数。

类组件的创建

编辑器快捷方式:rce

import React, { Component } from 'react'

export class ComponentClass extends Component {
  render() {
    return (
      <div>
        <h2>单文件类组件</h2>
      </div>
    )
  }
}

export default ComponentClass

类组件需要继承 Component ,因此需要进行导入,如果不导入,则需要继承 React.Component ,同时,类中需要调用 render() 渲染函数,在渲染函数中,return 返回对应的 JSX 。

组件的引入与使用

组件的使用也很简单,按模块化语法引入之后,直接当作标签在 JSX 中使用。

import React from 'react'
// 引入单文件组件
import ComponentClass from './ComponentClass'
import ComponentFun from './ComponentFun'


function App() {
  return (
    <div>
      {/* 使用组件 */}
      <ComponentClass />
      <ComponentFun />
    </div>
  )
}

export default App

因为 JSX 必须有一个根节点,因此在引入使用后,渲染的结果中,会出现多个 div  嵌套,比如下面这样子的:

<div id="root"><div><div><h2>单文件类组件</h2></div><div><h2>单文件函数组件</h2></div></div></div>

为了解决这个问题,React 提供了 “片段” 组件 Fragment ,它允许你将子列表分组,渲染后不会向 DOM 添加额外节点:

import React, { Component,Fragment } from 'react'

export class ComponentClass extends Component {
  render() {
    return (
      <Fragment>
        <h2>单文件类组件</h2>
      </Fragment>
    )
  }
}

export default ComponentClass

使用也很简单,就是引入后,将 JSX 中的div 根节点替换即可,最终渲染后的 DOM 如下:

<div id="root"><div><h2>单文件类组件</h2><h2>单文件函数组件</h2></div></div>

总结

上面主要介绍了两种不同组件的具体写法,以及组件的引入与使用,无论使用 Class 类组件还是函数组件,组件本身都是由 逻辑代码JSX 组成的,组件也是 React 的基本单位,一个应用是由多个组件嵌套堆叠而成的。

组件间的值传递 Props

父组件向子组件传值 -普通传值

父级组件传递数据

默认情况由父级组件传递数据到子级组件,我们将需要传递的数据,以属性的方式,写入组件中,如下:

import React from 'react'
// 引入单文件组件
import PropsClass from './PropsClass'
import PropsFun from './PropsFun'

// 要传递的数据
const toData = [
  {id:1,name:"刘能",age:66},
  {id:2,name:"广坤",age:16}
]

function App() {
  return (
    <div>
      {/* 将需要传递的数据,以属性的方式,写入组件 */}
      <PropsClass toClass={toData} />
      <PropsFun toFun={toData} />
    </div>
  )
}

export default App

这样就完成了父级组件向子级组件传递数据的任务。那么组件又分为 函数组件和类组件,下面,我们分别展示 类组件和函数组件,是如何获取传递进来的数据的。

我们先看类组件的获取方式:

class 子级组件接受数据

class 组件中使用 this.props.xx属性名 获取父级组件传递的数据:

import React, { Component, Fragment } from 'react'

export class PropsClass extends Component {
  render() {
    return (
      <Fragment>
        <h1>接受Props 数据</h1>
        {console.log(this.props.toClass)} {/* 打印数据 */}
        {/* 遍历数据 */}
        {this.props.toClass.map(item =>
        (
          <div key={item.id}>
            <span>{item.name}</span><span>{item.age}</span>
          </div>
        )
        )}
      </Fragment>
    )
  }
}

export default PropsClass

类组件中 this 操作相对容易,因此,React 默认会将父级组件的传入的数据放入 props 属性中,而在类组件中,如代码所示,我们就可以直接使用 this.props 来获取数据了。

函数子级组件接受数据

函数组件中,Props 数据会默认传入函数,因此需要在函数形参中获取,直接使用即可。

import React, { Fragment } from 'react'

// 函数形参获取 Props 传值
function PropsFun(props) {
  return (
    <Fragment>
      <h1>函数接受 Props </h1>
      {console.log(props.toFun)}
      {/* 遍历数据 */}
      {props.toFun.map(item=>
        (
        <div key={item.id}>
          <span>{item.name}</span>
        </div>
        )
      )}
    </Fragment>
  )
}

export default PropsFun

前面我们学习了父级组件向不同的子级组件传递数据,以及子级组件如何接受数据并处理,而如果父级组件传递较为复杂的数据时,如何传递数据,如何在子组件中使用,就需要我们进一步学习了解。

父组件向子组件传值 -解构传值

父级组件传递数据

传递普通数据,前面我们已经接触过了,如果要是传递的数据是数组或者对象,我们应该如何处理呢?

最直接的办法就是在传递时,在父级组件中将数据先进行解构,因为解构出来的数据,正好就是符合组件 “属性” 写法的:

import React from 'react'
// 引入单文件组件
import PropsClass from './PropsClass'
import PropsFun from './PropsFun'

// 要传递的数据
const toData = [
  {id:1,name:"刘能",age:66},
  {id:2,name:"广坤",age:16}
]

function App() {
  return (
    <div>
      {/* 结构数据并传入 */}
      <PropsClass {...toData[0]} />
      <PropsFun {...toData[1]} />
    </div>
  )
}

export default App

上面是解构传参,而在子级组件中应用时,与普通的应用并没有区别,按照解构好的对应格式进行接收就可以了,下面我们分别展示 类组件和函数组件中获取解构传参的方式

class 子级组件接受数据

依然使用 props 获取传参。

import React, { Component, Fragment } from 'react'

export class PropsClass extends Component {
  
  render() {
    // 获取传入的解构数据
    const {name,age} = this.props
    return (
      <Fragment>
        <h1>Class 接受Props 数据</h1>
        {console.log(name,age,'--')} {/* 打印数据 */}
        
      </Fragment>
    )
  }
}

export default PropsClass

函数子级组件接受数据

依然使用函数形参获取数据。

import React, { Fragment } from 'react'

// 函数形参获取 Props 传值 (结构)
function PropsFun({ name, age }) {
  return (
    <Fragment>
      <h1>函数接受 Props </h1>
      fun 数据:
      {console.log(age, name)}
      <div>
        <span>{name}</span> 
        <span>{age}</span> 
      </div>
    </Fragment>
  )
}

export default PropsFun

设置默认值

在一定的条件下,父级组件即便没有传入数据,子组件依然需要展示相关内容,那么此时,我们就可以在子组件中设置默认值来填充,当父级组件没有传入数据时,子组件使用默认数据,而如果父级组件有数据传入,则替换默认值。

父级组件可以传入数据,也可以不传入:

import React from 'react'
// 引入单文件组件
import PropsClass from './PropsClass'
import PropsFun from './PropsFun'

function App() {
  return (
    <div>
      {/* 父级组件没有传值则使用子组件的默认值,传递则替换 */}
      <PropsClass names="llll"/>
      <PropsFun />
    </div>
  )
}

export default App

类组件设置默认值

class 子组件中使用 static defaultProps 设置默认值,当然,我们依然需要从 this.props 中获取。

import React, { Component, Fragment } from 'react'

export class PropsClass extends Component {
  
  // 此时我们就设置了 props 的默认值,
  // 如果 父组件没有传递数据,则默认使用
  // 如果传递了数据,则替换默认值
  static defaultProps = {
    names:'西岭老湿',
    age:18
  }


  render() {
    // 获取组件传入的数据,可能是默认值,也可能是传入的数据
    const {names,age} = this.props
    return (
      <Fragment>
        <h2>Class 组件</h2>
        <p>{names}</p>
        <p>{age}</p>
      </Fragment>
    )
  }
}

export default PropsClass

函数组件设置默认值

函数组件需要使用 组件名.defaultProps 设置一个对象作为默认值,依然使用形参获取:

import React, { Fragment } from 'react'

// 函数形参获取 Props 传值 (结构)
function PropsFun({ name, age }) {
  return (
   <div>
     <h2>函数组件</h2>
     <p>{name}</p>
     <p>{age}</p>
   </div>
  )
}

// 函数组件需要使用 组件名.defaultProps 设置一个对象
PropsFun.defaultProps = {
  name:'西岭',
  age:16
}

export default PropsFun

如果不想在子组件的形参接收时解构,也可以直接获取 props

import React, { Fragment } from 'react'

// 函数形参获取 Props 传值 (结构)
function PropsFun(props) {
  return (
   <div>
     <h2>函数组件</h2>
     <p>{props.name}</p>
     <p>{props.age}</p>
   </div>
  )
}

// 函数组件需要使用 组件名.defaultProps 设置一个对象
PropsFun.defaultProps = {
  name:'西岭',
  age:16
}

export default PropsFun

向子组件传递 JSX

父级组件传递 JSX

在父级组件中,需要向子级组件传递 JSX ,需要将 jsx 写在组件的双标签内

import React from 'react'
// 引入单文件组件
import PropsClass from './PropsClass'
import PropsFun from './PropsFun'

function App() {
  return (
    <div>
      <h1>我是App</h1>
      {/* 需要传递 JSX ,写在组件双标签内 */}
      <PropsClass>
        {/* 可以传递多个标签 */}
        <p>父级组件中传入的 JSX, p标签,App-Class组件</p>
        <span>父级组件中传入的 JSX,span标签,App-Class组件</span>
      </PropsClass>
      <PropsFun />
    </div>
  )
}

export default App

class 子组件接收 JSX

使用 this.props.children 可以接收父级组件中传入的全部 JSX

import React, { Component, Fragment } from 'react'
export class PropsClass extends Component {
  render() {
   
    return (
      <Fragment>
        <h2>Class 组件</h2>
        {/* 接收 JSX ,可以接收多个 */}
        {this.props.children}
      </Fragment>
    )
  }
}

export default PropsClass

函数子组件接收 JSX

函数组件中获取 jsx ,可以直接使用 props 接收参数

import React, { Fragment } from 'react'

// 函数组件中获取 jsx ,可以直接使用 props 接收参数
function PropsFun(props) {
  return (
   <div>
     <h2>函数组件</h2>
     <p>{props.name}</p>
     <p>{props.age}</p>
     {props.children}
   </div>
  )
}

// 函数组件需要使用 组件名.defaultProps 设置一个对象
PropsFun.defaultProps = {
  name:'西岭',
  age:16
}

export default PropsFun

总结

组件是 React 的核心,而组件中维护的数据,决定了 UI 的渲染,有一个公式 UI = render(data/state) 很贴切地表示了它们之间的关系,因此,数据在 React 中是非常重要的。本篇内容,我们简单了解了组件间的数据传递及使用方式,数据在组件间传递还有一个专业名词叫 ‘数据流’,这个内容是非常多的,后面我还会写的!