资讯专栏INFORMATION COLUMN

React 深入系列7:React 常用模式

Chao / 623人阅读

摘要:本篇是深入系列的最后一篇,将介绍开发应用时,经常用到的模式,这些模式并非都有官方名称,所以有些模式的命名并不一定准确,请读者主要关注模式的内容。

React 深入系列,深入讲解了React中的重点概念、特性和模式等,旨在帮助大家加深对React的理解,以及在项目中更加灵活地使用React。

本篇是React深入系列的最后一篇,将介绍开发React应用时,经常用到的模式,这些模式并非都有官方名称,所以有些模式的命名并不一定准确,请读者主要关注模式的内容。

1. 受控组件

React 组件的数据流是由state和props驱动的,但对于input、textarea、select等表单元素,因为可以直接接收用户在界面上的输入,所以破坏了React中的固有数据流。为了解决这个问题,React引入了受控组件,受控组件指input等表单元素显示的值,仍然是通过组件的state获取的,而不是直接显示用户在界面上的输入信息。

受控组件的实现:通过监听表单元素值的变化,改变组件state,根据state显示组件最终要展示的值。一个简单的例子如下:

class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: ""};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert("A name was submitted: " + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      
); } }

和受控组件对应的概念是非受控组件,非受控组件通过ref获取表单元素的值,在一些场景下有着特有的作用(如设置表单元素的焦点)。

2. 容器组件

容器组件和展示组件是一组对应的概念,关注的是组件逻辑和组件展示的分离。逻辑由容器组件负责,展示组件聚焦在视图层的展现上。在React 深入系列2:组件分类中已对容器组件和展示组件作过详细介绍,这里不再赘述。

3. 高阶组件

高阶组件是一种特殊的函数,接收组件作为输入,输出一个新的组件。高阶组件的主要作用是封装组件的通用逻辑,实现逻辑的复用。在React 深入系列6:高阶组件中已经详细介绍过高阶组件,这里也不再赘述。

4. Children传递

首先,这个模式的命名可能并不恰当。这个模式中,借助React 组件的children属性,实现组件间的解耦。常用在一个组件负责UI的框架,框架内部的组件可以灵活替换的场景。

一个示例:

// ModalDialog.js
export default function ModalDialog({ children }) {
  return 
{ children }
; }; // App.js render() { }

ModalDialog组件是UI的框,框内组件可以灵活替换。

5. Render Props

Render Props是把组件部分的渲染逻辑封装到一个函数中,利用组件的props接收这个函数,然后在组件内部调用这个函数,执行封装的渲染逻辑。

看一个官方的例子:

class Cat extends React.Component {
  render() {
    const mouse = this.props.mouse;
    return (
      
    );
  }
}

class Mouse extends React.Component {
  constructor(props) {
    super(props);
    this.handleMouseMove = this.handleMouseMove.bind(this);
    this.state = { x: 0, y: 0 };
  }

  handleMouseMove(event) {
    this.setState({
      x: event.clientX,
      y: event.clientY
    });
  }

  render() {
    return (
      
{/* * Mouse组件并不知道应该如何渲染这部分内容, * 这部分渲染逻辑是通过props的render属性传递给Mouse组件 */} {this.props.render(this.state)}
); } } class MouseTracker extends React.Component { render() { return (

Move the mouse around!

( )}/>
); } }

Mouse监听鼠标的移动,并将鼠标位置保存到state中。但Mouse组件并不知道最终要渲染出的内容,需要调用this.props.render方法,执行渲染逻辑。本例中,Cat组件会渲染到鼠标移动到的位置,但完全可以使用其他效果来跟随鼠标的移动,只需更改render方法即可。由此可见,Mouse组件只关注鼠标位置的移动,而跟随鼠标移动的界面效果,由使用Mouse的组件决定。这是一种基于切面编程的思想(了解后端开发的同学应该比较熟悉)。

使用这种模式,一般习惯将封装渲染逻辑的函数赋值给一个命名为render的组件属性(如本例所示),但这并不是必需,你也可以使用其他的属性命名。

这种模式的变种形式是,直接使用React组件自带的children属性传递。上面的例子改写为:

class Mouse extends React.Component {
  // 省略

  render() {
    return (
      
{/* * Mouse组件并不知道应该如何渲染这部分内容, * 这部分渲染逻辑是通过props的children属性传递给Mouse组件 */} {this.props.children(this.state)}
); } } Mouse.propTypes = { children: PropTypes.func.isRequired }; class MouseTracker extends React.Component { render() { return (

Move the mouse around!

{mouse => ( )}
); } }

注意children的赋值方式。

React Router 和 React-Motion 这两个库都使用到了Render Props模式。很多场景下,Render Props实现的功能也可以通过高阶组件实现。本例也可以用高阶组件实现,请读者自行思考。

6. Provider组件

这种模式借助React的context,把组件需要使用的数据保存到context,并提供一个高阶组件从context中获取数据。

一个例子:

先创建MyProvider,将共享数据保存到它的context中,MyProvider一般作为最顶层的组件使用,从而确保其他组件都能获取到context中的数据:

import React from "react";
import PropTypes from "prop-types";

const contextTypes = {
  sharedData: PropTypes.shape({
    a: PropTypes.bool,
    b: PropTypes.string,
    c: PropTypes.object
  })
};

export class MyProvider extends React.Component {

  static childContextTypes = contextTypes;

  getChildContext() {
    // 假定context中的数据从props中获取
    return { sharedData: this.props.sharedData };
  }

  render() {
    return this.props.children;
  }
}

然后创建高阶组件connectData,用于从context中获取所需数据:

export const connectData = WrappedComponent =>
  class extends React.Component {
    static contextTypes = contextTypes;

    render() {
      const { props, context } = this;
      return ;
    }
  };

最后在应用中使用:

const SomeComponentWithData = connectData(SomeComponent)

const sharedData = {
    a: true,
    b: "react",
    c: {}
};

class App extends Component {
  render() {
    return (
      
        
      
    );
  }
}

Provider组件模式非常实用,在react-redux、mobx-react等库中,都有使用到这种模式。

React 深入系列文章到此完结,希望能帮助大家更加深入的理解React,更加纯熟的应用React。

我的新书《React进阶之路》已上市,请大家多多支持!
链接:京东 当当

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/95195.html

相关文章

  • React 深入系列6:高阶组件

    摘要:在项目中用好高阶组件,可以显著提高代码质量。高阶组件的定义类比于高阶函数的定义。高阶函数接收函数作为参数,并且返回值也是一个函数。 React 深入系列,深入讲解了React中的重点概念、特性和模式等,旨在帮助大家加深对React的理解,以及在项目中更加灵活地使用React。 1. 基本概念 高阶组件是React 中一个很重要且比较复杂的概念,高阶组件在很多第三方库(如Redux)中都...

    2shou 评论0 收藏0
  • 前端文档收集

    摘要:系列种优化页面加载速度的方法随笔分类中个最重要的技术点常用整理网页性能管理详解离线缓存简介系列编写高性能有趣的原生数组函数数据访问性能优化方案实现的大排序算法一怪对象常用方法函数收集数组的操作面向对象和原型继承中关键词的优雅解释浅谈系列 H5系列 10种优化页面加载速度的方法 随笔分类 - HTML5 HTML5中40个最重要的技术点 常用meta整理 网页性能管理详解 HTML5 ...

    jsbintask 评论0 收藏0
  • 前端文档收集

    摘要:系列种优化页面加载速度的方法随笔分类中个最重要的技术点常用整理网页性能管理详解离线缓存简介系列编写高性能有趣的原生数组函数数据访问性能优化方案实现的大排序算法一怪对象常用方法函数收集数组的操作面向对象和原型继承中关键词的优雅解释浅谈系列 H5系列 10种优化页面加载速度的方法 随笔分类 - HTML5 HTML5中40个最重要的技术点 常用meta整理 网页性能管理详解 HTML5 ...

    muddyway 评论0 收藏0
  • 深入理解JavaScript

    摘要:深入之继承的多种方式和优缺点深入系列第十五篇,讲解各种继承方式和优缺点。对于解释型语言例如来说,通过词法分析语法分析语法树,就可以开始解释执行了。 JavaScript深入之继承的多种方式和优缺点 JavaScript深入系列第十五篇,讲解JavaScript各种继承方式和优缺点。 写在前面 本文讲解JavaScript各种继承方式和优缺点。 但是注意: 这篇文章更像是笔记,哎,再让我...

    myeveryheart 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<