万事开头难。React 也不例外,作为初学者,我们也有一大堆问题。我应该将数据放在何处?如何进行变化通知?如何管理状态?这些问题的答案往往与上下文有关,而有时取决于 React 的实战经验。但是,有一种广泛使用的模式,有助于组织基于 React 的应用,那便是将组件分为展示型组件和容器型组件。
我们先从一个简单示例开始,首先说明此示例的问题,然后将组件分成容器型组件和展示型组件。示例中使用的是 Clock 组件,它接收 Date 对象作为属性并显示实时时间。
Clock
class Clock extends React.Component { constructor(props) { super(props); this.state = { time: this.props.time }; this._update = this._updateTime.bind(this); } render() { const 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) }); } }; ReactDOM.render(<Clock time={ new Date() }/>, ...);
在组件的构造函数中,我们初始化了组件的状态,这里只保存了当前时间。通过使用 setInterval ,我们每秒更新一次状态,然后组件会重新渲染。要想看起来像个真正的时钟,我们还使用了两个辅助函数: _formatTime 和 _updateTime 。_formatTime 用来提取时分秒并确保它们是两位数的形式。_updateTime 用来将 time 对象设置为当前时间加一秒。
setInterval
_formatTime
_updateTime
time
这个组件中做了好几件事,它似乎承担了太多的职责。
容器型组件知道数据及其结构,以及数据的来源。它们知道是如何运转的,或所谓的业务逻辑。它们接收信息并对其进行处理,以方便展示型组件使用。通常,我们使用 高阶组件 来创建容器型组件,因为它们为我们的自定义逻辑提供了缓冲区。
下面是 ClockContainer 的代码:
ClockContainer
// 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) }); } };
它接收 time (date 对象) 属性,使用 setInterval 循环并了解数据 (getHours、getMinutes 和 getSeconds) 的详情。最后渲染展示型组件并传入时分秒三个数字。这里没有任何展示相关的内容。只有业务逻辑。
getHours
getMinutes
getSeconds
展示型组件只涉及组件的外在展现形式。它们会有附加的 HTML 标记来使得页面更加漂亮。这种组件没有任何绑定及依赖。通常都是实现成 无状态组件,它们没有内部状态。
在本示例中,展示型组件只包含两位数的检查并返回 <h1> 标签:
<h1>
// 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>; };
将组件分成容器型组件和展示型组件可以增加组件的可复用性。不改变时间或不使用 JavaScript Date 对象的应用中,都可以使用 Clock 函数/组件。原因是它相当单纯,不需要对所需数据的详情有任何了解。
容器型组件封装了逻辑,它们可以搭配不同的展示型组件使用,因为它们不参与任何展示相关的工作。我们上面所采用的方法是一个很好的示例,它阐明了容器型组件是如何不关心展示部分的内容的。我们可以很容易地从数字时钟切换到模拟时钟,唯一的变化就是替换 render 方法中的 <Clock> 组件。
render
<Clock>
测试也将变得更容易,因为组件承担的职责更少。容器型组件不关心 UI 。展示型组件只是纯粹地负责展示,它可以很好地预测出渲染后的 HTML 标记。
容器型和展示型的并非是新概念,但是它真的非常适合 React 。它使得应用具有更好的结构,易于管理和扩展。
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8