React 组件

目录

  • 前言
  • 一、React 内置组件
    • 1、PureComponent 组件
    • 2、 Fragment 组件
  • 二、function 组件和 class 组件
    • 1、function 组件
      • (1)、将 function 组件转换成 class 组件
    • 2、class 组件
      • (1)、super() 里边到底要不要传入 props 参数?
      • (2)、类组件中到底要不要定义构造函数 constructor() ?
      • (3)、绑定事件到底要不要在构造函数 constructor() 中进行?
        • ①、bind 指定 this 指向
        • ②、箭头函数定义方法保证 this 指向不变
  • 三、组件的生命周期
  • 四、组件的 props 对象和 state 对象
    • 1、props 对象
      • (1)、使用 props 对象
      • (2)、为 props 设置默认值
      • (3)、props 的类型检查——PropTypes
    • 2、state 对象
      • (1)、使用 state 对象
      • (2)、我应该如何更新那些依赖于当前的 state 的 state 呢?
  • 五、React 组件的 API
    • 1、setState 方法
    • 2、replaceState 方法
    • 3、setProps 方法
    • 4、replaceProps 方法
    • 5、forceUpdate 方法
  • 六、React 组件间的通信
    • 1、props 对象
    • 2、context 对象
      • (1)、Context API
        • ①、React.createContext
        • ②、Context.Provider 和 Context.Consumer
      • (2)、Context 的使用案例
        • ①、简单使用 Context 的案例
        • ②、动态使用 Context 的案例
    • 3、组件组合——只为了避免层层传递
    • 4、refs
  • 七、React 组件进阶
    • 1、React 高阶组件
    • 2、将函数作为子类的组件


前言

在 react 中 UI = f(data),这是 React 的核心理念之一。

一个 React 组件就是一个 JavaScript 函数,并且你可以在函数中书写 markup——主要指的是 HTML 等 “标记语言” 代码。React 的 markup 类似 Vue 的 vnode 对象。

React 组件官方推荐采用 JSX 语法,具体请看:在 React 中使用 JSX。


一、React 内置组件

React 内置组件的使用格式:React.<内置组件>

React 内置组件包括:

  • Component 组件:通过继承该组件可以创建一个 class 组件。
  • Smart 组件:
    • Smart 组件又称为 容器组件,它负责处理应用的数据以及业务逻辑,同时将状态数据与操作函数作为属性传递给子组件;
    • 一般而言它仅维护很少的 DOM,其所有的 DOM 也仅是作为布局等作用。
  • Dumb 组件:
    • Dumb 组件又称为 木偶组件,它负责展示作用以及响应用户交互,它一般是无状态的(在如 Modal 等类组件中可能会维护少量自身状态);
    • 一般而言 Dumb 组件会拆分为一个个可复用、功能单一的组件。因此 Dumb 组件使用函数式组件定义,当其需要对重渲染进行优化时则可以使用 PureComponent 组件。
  • PureComponent 组件:是对 Component 组件的性能优化。通过继承该组件可以创建一个 class 组件。
  • Fragment 组件:
    • 允许你将子列表分组,而无需向 DOM 添加额外节点。
    • 在不需要设置 key 属性的前提下,该组件可以简写为 <>

1、PureComponent 组件

PureComponent 组件是基于 Component 组件实现的,是对 Component 组件的性能优化。

通过 extends PureComponent 组件能够创建一个 class 组件。

Component 组件 和 PureComponent 组件的差异:

  • 在 Component 组件中
    • 生命周期 shouldComponentUpdate 钩子函数返回 true 时才会进行重渲染,如果返回 false 则不会进行重渲染,默认总是返回 true。
    • 有时我们需要在 shouldComponentUpdate 中进行逻辑判断,来自定义组件是否需要重渲染,以提升组件的性能。
  • 在 PureComponent 组件中
    • 自动通过 props 和 state 的浅对比来重写了 shouldComponentUpate 钩子——只有在 state 或 props 发生变化时 shouldComponentUpate 钩子才返回 true,默认是 false。
    • 不需要开发者自己实现 shouldComponentUpdate,就可以进行简单的判断来提升性能。

建议在编写 class 组件时,直接继承 PureComponent 组件即可。

例如:

class ChildComponent extends React.PureComponent {
  render() {
    return(
      <div>{this.props.numbers}</div>
    )
  }
}

2、 Fragment 组件

Fragments 组件允许你将子列表分组,而无需向 DOM 添加额外节点。

例如:

render() {
  return (
    <React.Fragment>
      <ChildA />
      <ChildB />
      <ChildC />
    </React.Fragment>
  );
}

在不需要设置 key 属性的情况下, 组件可以简写为 <>

render() {
  return (
    <>
      {condition ? (
        <ChildA />
      ) : (
        <ChildB />
      )}
    </>
  );
}

当需要给 Fragments 标签添加 key 属性时,不能使用简短的“空标签”语法,必须显示的使用 React.Fragments:

render() {
  <div>
    {arr.map((item, idx) => {
      return (
        <React.Fragment key={idx}>
          {condition ? (
            <ChildA />
          ) : (
            <ChildB />
          )}
        </React.Fragment>
      );
    })
  </div>
}

二、function 组件和 class 组件

  • function 组件:
    • 无需使用 render() 方法,直接访问 props 即可。
  • class 组件:
    • 使用 ES6 class 语法 extends React 的 PureComponent 或 Component 组件。
    • 需要使用 render() 方法,在 render() 方法中要用 this 才能访问 props。

1、function 组件

function 组件是创建 React 组件的最简单方式。

例如:

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

(1)、将 function 组件转换成 class 组件

通过以下 5 步将 function 组件转成 class 组件:

  • 创建一个同名的 ES6 class,并且继承于 React.Component。
  • 添加一个空的 render() 方法。
  • 将函数体移动到 render() 方法之中。
  • 在 render() 方法中使用 this.props 替换 props。
  • 删除剩余的空函数声明。

2、class 组件

React 的 class 组件使用的是 ES6 的 class 语法,通过 extends React 的 PureComponent 或 Component 组件来定义的。

例如:

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

(1)、super() 里边到底要不要传入 props 参数?

若显示声明了 constructor 方法,则必须要调用super;
在组件的非 constructor 方法中 使用 props 时,可不用传入,直接使用;
在 constructor 内使用 props ,则必须要将 props 传入 super 中。
以上说法,在ES7之后是无效的。(待研究)

(2)、类组件中到底要不要定义构造函数 constructor() ?

ES6 中新增了类的概念,一个类必须要有 constructor 方法,如果在类中没有显示定义,则一个空的 constructor 方法会被默认添加;

一般需要在构造函数中初始化state和绑定事件,因此当需要初始化state或绑定事件时,需要显示定义 constructor 方法,并在 constructor 方法中初始化 state 和绑定事件。

例如:

import React from "react";
 
export default class MyComponent extends React.Component {
  construction() {
    super();
    this.state = {
      name: "Mike"
    };
  }
  render() {
    return (
      <div>{this.state.name}</div>
    )
  }
}

ES7+ 简化了上述写法,规定 class 中可以不实现 constructor 方法,state 这样定义(据说会有一些问题,请参阅此文):

import { PureComponent } from "react";
 
export default class MyComponent extends PureComponent {
  state = {
    name: "Mike"
  };
  render() {
    return (
      <div>{this.state.name}</div>
    )
  }
}

(3)、绑定事件到底要不要在构造函数 constructor() 中进行?

一般需要在构造函数中绑定事件,但需要使用 bind,如果不想调用 bind,也可以使用箭头函数来定义函数(箭头函数不会改变 this 指向)。

举例说明:

DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Reacttitle>
  head>
  <body>
    <div id="example">div>
    <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js">script>
    <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js">script>
    <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js">script>
    <script type="text/babel">
      class Apple extends React.Component {
        constructor(props){
          super(props);
          this.state = {
            name: '张三'
          }
        }
        handlerClick(){
          console.log(this.state.name);
        }
        render () {
          return (
            <button onClick = { this.handlerClick }>点我呀</button>
          )
        }
      }
      ReactDOM.render(
        <div>
          <Apple />  
        </div>, 
        document.getElementById("example")
      );
    script>
  body>
html>

运行上述代码是会报错的。解决办法有两种:

  • bind 指定 this 指向;
  • 箭头函数定义方法保证 this 指向不变。

①、bind 指定 this 指向

方式一:直接在 constructor 里使用 bind 绑定 this

constructor(props){
  super(props);
  this.state = {
    name: '张三'
  }
  this.handlerClick=this.handlerClick.bind(this);
}
handlerClick(){
  console.log(this.state.name);
}
render () {
  return (
    <button onClick = { this.handlerClick }>点我呀</button>
  )
}

但是这样又一个弊端:该方法会在页面第一次渲染时被执行一次。 可以跟“方式二”对比一下。

方式二:在 render 函数里调用方法时绑定 this

handlerClick(){
  console.log(this.state.name);
}
render () {
  return (
    <button onClick = { this.handlerClick.bind(this) }>点我呀</button>
  )
}

②、箭头函数定义方法保证 this 指向不变

方式一:直接在 render 函数外用箭头函数定义方法——使用 class fields 绑定回调函数

handlerClick = () => {
  console.log(this.state.name);
}
render () {
  return (
    <button onClick = { this.handlerClick }>点我呀</button>
  )
}

方式二:直接在 render 函数里用箭头函数定义并使用方法——在回调中使用箭头函数

render () {
  return (
    <button onClick = { () => {
       console.log(this.state.name);
    }}>点我呀</button>
  )
}

【注意】若采用在回调中使用箭头函数,当回调函数作为一个属性值传入低阶组件,上述这种方法可能会进行额外的重新渲染。建议使用 class fields 绑定回调函数 来避免这类性能问题。


三、组件的生命周期

React 组件的生命周期


四、组件的 props 对象和 state 对象

React 官方:组件状态
Props vs State
ReactJS: Props vs. State

props 对象和 state 对象都是用来保存信息的,这些信息可以控制组件的渲染输出。当 props 和 state 的任一个发生变化都会触发组件的渲染更新。我们只需要通过更新该组件的 state 和其子组件的 props 就能重新渲染用户界面,尽量避免直接操作真实 DOM。

props 对象和 state 对象的一个重要的不同点就是:

  • props 是传递给组件的(类似于函数的形参)。React 组件不能改变自身的 props 对象,只可以父组件改变传递给子组件的 props 对象。
  • state 是在组件内被组件自己管理的(类似于在一个函数内声明的变量)。React 组件可以改变自身的 state 对象。

1、props 对象

因为 React 组件就是 JavaScript 函数,所以 props 对象可以理解为“函数的形参”。

React 里任何组件都不能修改自身的 props 对象——像这样的函数被称为“纯函数”——该函数不会尝试更改入参,且多次调用下相同的入参始终返回相同的结果。

(1)、使用 props 对象

function HelloMessage(props) {
    return <h1>Hello {props.name}!</h1>;
}
 
const element = <HelloMessage name="hello"/>;
 
ReactDOM.render(
    element,
    document.getElementById('example')
);

(2)、为 props 设置默认值

通过组件类的 defaultProps 属性为 props 设置默认值。

class Welcome extends React.Component {
  render() {
    return <h1>Hello {this.props.name}</h1>;
  }
}

Welcome.defaultProps = {
  name: "world",
};

(3)、props 的类型检查——PropTypes

你可以借助第三方的 Flow 或 TypeScript 等 JavaScript 扩展来对整个应用程序做类型检查,也可以直接使用 React 内置的 PropTypes 功能来做 props 的类型检查。这里只看 PropTypes。

React.PropTypes 在 React v15.5 版本后已经移到了 prop-types 库。

react 使用 PropTypes 进行类型检查
React 官方提供了 PropTypes 使用不同验证器的例子

React 支持使用 propTypes 对 props 进行验证。它可以保证我们的应用组件被正确使用。当向 props 传入无效数据时,JavaScript 控制台会抛出警告。

首先要安装 prop-types 包:

npm i -S prop-types

然后使用 prop-types:

import PropTypes from 'prop-types';

let title = "qwert";
// let title = 123;
class MyTitle extends React.Component {
  render() {
    return (
      <h1>Hello, {this.props.title}</h1>
    );
  }
}
 
MyTitle.propTypes = {
  title: PropTypes.string
};

2、state 对象

state 是私有的,并且完全受控于当前组件。

构造函数(constructor)是唯一可以给 this.state 赋值的地方。之后,不要直接修改 State 对象,而是应该使用 setState() 方法来更新 state 对象(具体请看下文的 React 组件的 API 部分)。

当你调用 setState() 的时候,React 会把你提供的对象合并到当前的 state 对象。

(1)、使用 state 对象

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

  componentWillUnmount() {
    clearInterval(this.timerID);
  }

  tick() {
    this.setState({
      date: new Date()
    });
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

(2)、我应该如何更新那些依赖于当前的 state 的 state 呢?

给 setState 传递一个函数,而不是一个对象,就可以确保每次的调用都是使用最新版的 state。(具体请看下文的 React 组件的 API 部分)

【推荐文章】:
React 官方:组件状态
Props vs State
ReactJS: Props vs. State


五、React 组件的 API

React 组件 API

  • setState:设置状态。
  • replaceState:替换状态。
  • setProps:设置属性。
  • replaceProps:替换属性。
  • forceUpdate:强制更新。
  • findDOMNode:获取 DOM 节点(详见react 与 DOM)。

1、setState 方法

React 官方:组件状态

setState 方法是 React 事件处理函数 和 请求回调函数 中触发 UI 更新的主要方法。

setState 方法可以接收 2 个参数:

  • nextState:将要设置的新状态,该状态会和当前的 state 合并。
  • callback:可选参数,回调函数。该函数会在 setState 设置成功,且组件重新渲染后调用。

默认情况下,setState() 总是会触发一次组件重绘,除非在 shouldComponentUpdate() 钩子函数中实现了自定义一些条件渲染逻辑。

因为 this.props 和 this.state 可能会异步更新,所以你不要依赖他们的值来更新下一个状态。否则可能会导致无法更新。要解决这个问题,可以让 setState() 接收一个函数而不是一个对象。传递一个函数可以让你在函数内访问到当前最新的 state 的值。

例如:

class Counter extends React.Component{
  constructor(props) {
      super(props);
      this.state = {clickCount: 0};
      this.handleClick = this.handleClick.bind(this);
  }
  
  handleClick() {
    // setState 接收一个函数而不是一个对象作为参数
    this.setState(function(state) {
      return {clickCount: state.clickCount + 1};
    });
  }
  render () {
    return (<h2 onClick={this.handleClick}>点我!点击次数为: {this.state.clickCount}</h2>);
  }
}
ReactDOM.render(
  <Counter />,
  document.getElementById('example')
);

2、replaceState 方法

replaceState 方法与 setState 类似,但是方法只会保留 nextState 中状态,原 state 不在 nextState 中的状态都会被删除。

replaceState 方法接收 2 个参数:

  • nextState:将要设置的新状态,该状态会替换当前的 state。
  • callback:可选参数,回调函数。该函数会在 replaceState 设置成功,且组件重新渲染后调用。

3、setProps 方法

设置组件属性,并重新渲染组件。

当我们需要向组件传递数据或通知React.render()组件需要重新渲染,可以使用 setProps() 方法。

setProps 方法接收 2 个参数:

  • nextProps,将要设置的新属性,该状态会和当前的props合并
  • callback,可选参数,回调函数。该函数会在setProps设置成功,且组件重新渲染后调用。

4、replaceProps 方法

replaceProps 方法与 setProps 方法类似,但它会删除原有 props。

replaceProps 方法接收 2 个参数:

  • nextProps,将要设置的新属性,该属性会替换当前的props。
  • callback,可选参数,回调函数。该函数会在replaceProps设置成功,且组件重新渲染后调用。

5、forceUpdate 方法

forceUpdate 方法会使组件调用自身的 render() 函数重新渲染组件,组件的子组件也会调用自己的 render() 函数。但是,组件重新渲染时,依然会读取 this.props 和 this.state,如果状态没有改变,那么 React 只会更新 DOM。

forceUpdate 方法接收 1 个参数:

  • callback,可选参数,回调函数。该函数会在组件render()方法调用后调用。

forceUpdate 方法适用于 this.props 和 this.state 之外的组件重绘(如:修改了this.state后),通过该方法通知 React 需要调用 render() 函数。

一般来说,应该尽量避免使用 forceUpdate 方法,而仅从 this.props 和 this.state 中读取状态并由 React 触发 render() 函数的调用。

【拓展】更新 React 组件的方式:

  • 可以在节点上再次调用 React.render() 方法,触发组件重新渲染。
  • 可以通过 setProps() 方法改变组件属性,触发组件重新渲染。
  • 可以使用 forceUpdate() 方法强制触发组件重新渲染,不过应尽量避免这样做。

六、React 组件间的通信

一般的,react 组件间的通信是通过 props 属性 自上而下(由父及子) 进行传递的。

React 跨组件通信,一般有两种实现方式:

  • 使用 Context 实现 react 跨组件通信。
  • 使用组件组合实现 react 跨组件通信(将函数作为子类的组件)。

1、props 对象

在 React 中 props 对象可以用来实现父组件向子组件通信。

例如:

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

2、context 对象

React 官方:Context

Context 提供了一种在组件之间共享“应用程序中许多组件都需要的属性”的方式——将这个 “多个组件共享一个全局数据” 放在 Context 对象上。而不必显式地通过组件树的逐层传递 props。

【注意】
请谨慎使用 Context,因为这会使得组件的复用性变差。
如果你只是想避免层层传递一些属性,组件组合 有时候是一个比 context 更好的解决方案。

(1)、Context API

①、React.createContext

创建一个 Context 对象。

const MyContext = React.createContext(defaultValue);

createContext 函数:

  • 只有当组件所处的树中没有匹配到 Provider 时,其 defaultValue 参数才会生效。

②、Context.Provider 和 Context.Consumer

每个 Context 对象都会返回一个 Provider 组件,它允许 Consumer 组件订阅 context 的变化。

const MyContext = () => {
  return(
	<MyContext.Provider value={/* 某个值 */}>
	  <MyContext.Consumer>
        {value => /* 基于 context 值进行渲染*/}
	  </MyContext.Consumer>
    </MyContext.Provider>
  )
}

Provider 组件

  • Provider 接收一个 value 属性,传递给 Consumer 组件。
  • 一个 Provider 可以和多个 Consumer 组件有对应关系。
  • 多个 Provider 可以嵌套使用,里层的会覆盖外层的数据(就近原则)。
  • Provider 使用了与 Object.is 相同的算法,通过新旧值检测来确定变化。

当 Provider 的 value 值发生变化时,它内部的所有消费组件都会重新渲染。Provider 及其内部 consumer 组件都不受制于 shouldComponentUpdate 函数,因此当 consumer 组件在其祖先组件退出更新的情况下也能更新。

Consumer 组件

  • Consumer 组件用来订阅 context。

这种方法需要一个函数作为子元素(function as a child)。这个函数接收当前的 context 值,并返回一个 React 节点。传递给函数的 value 值等价于组件树上方离这个 context 最近的 Provider 提供的 value 值。如果没有对应的 Provider,value 参数等同于传递给 createContext() 的 defaultValue。

(2)、Context 的使用案例

①、简单使用 Context 的案例

import React from "react";

const MyContext = React.createContext("66");

export default class ReactContext extends React.Component {
	render() {
		return (
			<MyContext.Provider value={"999"}>
				<Text />
			</MyContext.Provider>
		)
	}
}

function Text() {
	return (
		<MyContext.Consumer>
			{value => <p>{value}</p>}
		</MyContext.Consumer>
	)
}

②、动态使用 Context 的案例

动态切换语言。

import React from "react";

const en = {
  submit: "Submit",
  cancel: "Cancel"
}
const cn = {
  submit: "提交",
  cancel: "取消"
}

const MyContext = React.createContext(en);

class DynamicContext extends React.Component {
  state = { local: cn };
  toggleLocal = () => {
    const local = this.state.local === en ? cn : en;
    this.setState({ local });
  }
  render() {
    return (
      <MyContext.Provider value={this.state.local}>
        <button onClick={this.toggleLocal}>切换语言</button>

        {this.props.children}

      </MyContext.Provider>
    )
  }
}

class LocalBtton extends React.Component {
  render() {
    return (
      <MyContext.Consumer>
        {opt => ( // 函数作为子组件
          <div>
            <button>{opt.cancel}</button>&nbsp;<button>{opt.submit}</button>
          </div>
        )}
      </MyContext.Consumer>
    )
  }
}

export default () => (
  <DynamicContext>
    <LocalBtton />
  </DynamicContext>
);

当 provider 的父组件进行重渲染时,可能会在 consumers 组件中触发意外的渲染。例如:

class App extends React.Component {
  render() {
    return (
      <MyContext.Provider value={{something: 'something'}}>
        <Toolbar />
      </MyContext.Provider>
    );
  }
}

解决办法:将 value 状态提升到父节点的 state 里。例如:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: {something: 'something'},
    };
  }
  render() {
    return (
      <MyContext.Provider value={this.state.value}>
        <Toolbar />
      </MyContext.Provider>
    );
  }
}

3、组件组合——只为了避免层层传递

组件组合:react 官方建议组件使用一个特殊的 children prop 来将他们的子组件传递到渲染结果中。(将函数作为子类的组件,参见下文“高阶组件”部分)

例如:

function FancyBorder(props) {
  return (
    <div className={'FancyBorder FancyBorder-' + props.color}>
      {props.children}
    </div>
  );
}
import FancyBorder from './FancyBorder';

function WelcomeDialog() {
  return (
    <FancyBorder color="blue">
      <h1 className="Dialog-title">
        Welcome
      </h1>
      <p className="Dialog-message">
        Thank you for visiting our spacecraft!
      </p>
    </FancyBorder>
  );
}

4、refs

您可以将 ref 指向任何值。但是,ref 最常见的用例是访问 DOM 元素,请:参见这篇文章。


七、React 组件进阶

参考:高阶组件

高阶组件函数作为子组件 提供了常规组件复用之外的新的模式去使用组件。

1、React 高阶组件

高阶组件是:参数为组件,返回值为新组件的函数。
React 的高阶组件可以看作是一个工厂函数,接收一个组件作为参数并返回一个具有新功能或特性的组件。

举个例子:

import React from "react";
import magicBox from "./magicBox"

class MyComponent extends React.Component{   
  render() { 
  	const { name } = this.props;
    return (  
        <div>{name}</div>
    );  
  }  
}
export default magicBox(MyComponent);

React 高阶组件的实现:

// ./magicBox/index.jsx
import { PureComponent } from "react";

export default function magicBox (WrappedComponent) {   
	return class extends PureComponent {
		state = {
			name: "Mike"
		};
		render() { 
			return (  
				<WrappedComponent name={this.state.name} {...this.props}/>
			);  
		}
	}
}

组件与高阶组件的区别:

  • 组件是将 props 转换为 UI,而高阶组件是将组件转换为另一个组件。
  • 高阶组件只是为它封装的组件传递一些额外的功能和数据,不会有自己的UI展现。

高阶组件的优势:

  • 高阶组件可以自己获取外部资源,传给封装的组件。
  • 高阶组件的控制权是使用者。
  • 高阶组件不必复用组件,而是对传入组件进行二次封装,赋予其新的功能和特性。

使用高阶组件的注意事项:

  • 不要在 render 方法中使用高阶组件。
  • 务必复制原始组件的静态方法。
  • Refs 不会被传递。

【注意】
React15.3中新加了一个 PureComponent 类。PureComponent 类 与 React.Component 类 都可以用来作为继承的基类,不同之处在于,PureComponent 类功能更强大,它可以减少不必要的 render 渲染次数。

2、将函数作为子类的组件

参考:
React 教程:函数作为子组件(Function as Child Components)
将函数作为子组件的组件

“将函数作为子类的组件”是接收一个函数当作子组件的组件。
其原理是:利用了 React 组件的一个内置 children 属性,将函数作为子类的组件来实现。
这得益于 react 的 prop-types 的支持。

举个例子:

import { PureComponent } from 'react';

class MyComponent extends PureComponent {   
  render() {  
    return (  
        <div>
          {this.props.children('Scuba Steve')}
        </div>
    );  
  }  
}

MyComponent.propTypes = {  
  children: React.PropTypes.func.isRequired,  
};

【注意】
从 React v15.5 开始 ,React.PropTypes 助手函数已被弃用,建议使用 prop-types 库来定义 contextTypes,即你需要手动引入 import PropTypes from 'prop-types';

上述代码可以改为:

import { PureComponent } from 'react';
import PropTypes from 'prop-types';

class MyComponent extends PureComponent {   
  render() {  
    return (  
        <div>
          {this.props.children('Scuba Steve')}
        </div>
    );  
  }  
}

MyComponent.propTypes = {  
  children: PropTypes.func.isRequired,  
};

由上述代码可知:通过函数创建子类组件的组件,可以将 父类组件 和 它们的子类组件 解耦,让设计者决定选用哪些参数,及怎么将参数应用于子类组件

使用者可能考虑以不同的方式使用该组件,比如:

<MyComponent>
  {(name) => (
    <div>{name}div>
  )}
MyComponent>

<MyComponent>
  {(name) => (
    <img src=’/scuba-steves-picture.jpg’ alt={name} />
  )}
MyComponent>

将函数作为子类的组件的优势:

  • 组合组件的开发人员可以快速传递和使用这些属性。
  • 函数作为子组件的模式不强制使用者如何利用其值,可以非常灵活的使用。

将函数作为子类的组件 与 react 高阶组件 的比较:

  • 使用者不需要创建另一个组件来决定如何应用从 “高阶组件” 传入的属性。高阶组件通常在它们所组成的组件上强制要求属性名。为了解决这个问题,许多“高阶组件”提供者提供了一个选择器函数,允许使用者选择需要的属性名称(想想 redux-connects 选择函数)。在函数作为子组件的模式不存在这个问题。
  • 不会污染 “props” 的命名空间,这允许您使用 “Ratio” 组件结合 “Pinch to Zoom” 组件使用,无论它们是否都有计算宽度。高阶组件带有他们对组成的组件施加了隐式约定,不幸的是,这可能意味着 prop 名称冲突。
  • 高阶组件在您的开发工具和组件本身中创建一个间接层,例如,一旦包含在高阶组件中,高阶组件中的设置常量将无法访问。例如:MyComponent.SomeContant = ‘SCUBA’;,然后由高阶组件包裹,export default connect(...., MyComponent);。你的常数就好像死了。如果没有高阶组件提供访问底层组件类的函数,则再也访问不到它。



【参考文章】:
React 官网
React 中文文档

你可能感兴趣的:(#,React.js,react,组件)