React-Bits展示与容器组件:分离关注点

【免费下载链接】react-bits ✨ React patterns, techniques, tips and tricks ✨ 【免费下载链接】react-bits 项目地址: https://gitcode.com/gh_mirrors/re/react-bits

你是否还在为React组件中数据逻辑与UI展示混杂导致维护困难而烦恼?本文将通过React-Bits项目中的patterns/8.presentational-vs-container.md方案,带你彻底掌握展示组件与容器组件的分离模式,实现关注点分离,让代码更清晰、更易维护。读完本文你将学会如何区分两类组件、各自的适用场景及具体实现方法。

问题:数据与逻辑交织的困境

在传统React组件开发中,我们常将数据处理和UI渲染混在一起,导致组件职责不明确。例如以下代码:

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {time: this.props.time};
    this._update = this._updateTime.bind(this);
  }

  render() {
    var time = this._formatTime(this.state.time);
    return (
      <h1>{ time.hours } : { time.minutes } : { time.seconds }</h1>
    );
  }

  componentDidMount() {
    this._interval = setInterval(this._update, 1000);
  }

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

  _formatTime(time) {
    var [ hours, minutes, seconds ] = [
      time.getHours(),
      time.getMinutes(),
      time.getSeconds()
    ].map(num => num < 10 ? '0' + num : num);

    return {hours, minutes, seconds};
  }

  _updateTime() {
    this.setState({time: new Date(this.state.time.getTime() + 1000)});
  }
}

这种写法使得组件既负责数据获取和状态管理(如componentDidMount中的定时器),又负责UI渲染(render方法),违反了单一职责原则。

解决方案:展示组件与容器组件分离

核心思想

展示组件(Presentational Component)专注于如何显示(UI渲染),容器组件(Container Component)专注于如何获取数据和处理逻辑。通过这种分离,我们可以实现关注点分离,提高代码复用性和可维护性。

容器组件:处理数据与逻辑

容器组件了解数据的来源和处理方式,负责数据获取、状态管理等逻辑,并将处理后的数据以props的形式传递给展示组件。它们通常是有状态的,使用类组件或React Hooks实现。

以下是时钟案例中的容器组件实现:

// Clock/index.js
import Clock from './Clock.jsx'; // 引入展示组件

export default class ClockContainer extends React.Component {
  constructor(props) {
    super(props);
    this.state = {time: props.time};
    this._update = this._updateTime.bind(this);
  }

  render() {
    return <Clock { ...this._extract(this.state.time) }/>;
  }

  componentDidMount() {
    this._interval = setInterval(this._update, 1000);
  }

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

  _extract(time) {
    return {
      hours: time.getHours(),
      minutes: time.getMinutes(),
      seconds: time.getSeconds()
    };
  }

  _updateTime() {
    this.setState({time: new Date(this.state.time.getTime() + 1000)});
  }
};

在这个例子中,ClockContainer处理了定时器逻辑(componentDidMountcomponentWillUnmount)和时间数据提取(_extract方法),并将小时、分钟、秒数作为props传递给展示组件Clock

展示组件:专注UI渲染

展示组件关注于UI的呈现,不关心数据的来源,只通过props接收数据并渲染。它们通常是无状态的,可以用函数组件实现。

以下是时钟案例中的展示组件实现:

// Clock/Clock.jsx
export default function Clock(props) {
  var [ hours, minutes, seconds ] = [
    props.hours,
    props.minutes,
    props.seconds
  ].map(num => num < 10 ? '0' + num : num);

  return <h1>{ hours } : { minutes } : { seconds }</h1>;
};

Clock组件仅接收hoursminutesseconds三个props,并负责将它们格式化为两位数后显示在页面上,不包含任何业务逻辑。

高级应用:灵活的容器组件

为了进一步提高容器组件的复用性,我们可以将其设计为接受展示组件作为参数的高阶函数。这样,同一个容器组件可以与不同的展示组件配合,实现不同的UI效果。

export default function (Component) {
  return class Container extends React.Component {
    render() {
      return <Component />;
    }
  }
}

例如,我们可以用同一个时间逻辑容器组件,分别配合数字时钟展示组件和模拟时钟展示组件,实现不同的UI展示效果,而无需修改容器组件的逻辑。

总结与最佳实践

两类组件的对比

特性 容器组件 展示组件
关注点 数据如何获取和处理 UI如何展示
数据源 从API、Redux等获取 从props接收
状态管理 通常有状态 通常无状态(纯函数)
代码位置 patterns/8.presentational-vs-container.md patterns/8.presentational-vs-container.md

何时使用哪种组件

  • 容器组件:当需要处理数据获取、状态管理、订阅事件等逻辑时使用。
  • 展示组件:当需要复用UI组件、关注样式和布局时使用。

项目中的应用

在React-Bits项目中,除了patterns/8.presentational-vs-container.md详细介绍了这种模式外,你还可以在styling/01.stateless-ui-components.md中找到更多关于无状态UI组件(展示组件)的实践,以及在patterns/5.event-handlers.md中学习事件处理逻辑如何在容器组件中实现。

通过采用展示与容器组件分离的模式,我们可以让React应用的代码结构更清晰,维护更方便,复用性更高。开始在你的项目中实践这种模式吧!

【免费下载链接】react-bits ✨ React patterns, techniques, tips and tricks ✨ 【免费下载链接】react-bits 项目地址: https://gitcode.com/gh_mirrors/re/react-bits

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐