高阶组件的概念
高阶组件就是一个 React 组件包裹着另外一个 React 组件
这种模式通常使用函数来实现,基本上是一个类工厂(是的,一个类工厂!),它的函数签名可以用类似 haskell 的伪代码表示
hocFactory:: W: React.Component => E: React.Component
其中 W (WrappedComponent) 指被包裹的 React.Component,E (EnhancedComponent) 指返回类型为 React.Component 的新的 HOC。
我们有意模糊了定义中“包裹”的概念,因为它可能会有以下两种不同的含义之一:
- Props Proxy: HOC 对传给 WrappedComponent W 的 porps 进行操作,
- Inheritance Inversion: HOC 继承 WrappedComponent W。
高阶组件的简单使用
- 假设,我们有一个
IntervalEnhance
组件,我们在CartItem
组件中导入它,并通过它来包裹原有的导出对象。
-
// src/components/cart-item.jsx import React from 'react'; import { IntervalEnhance } from "./interval-enhance.jsx"; // 1. 导入包裹组件 class CartItem extends React.Component { // component code here } export default IntervalEnhance(CartItem); // 2. 包裹原有的CartItem组件
现在我们将来编写这个
IntervalEnhance
组件。// src/components/interval-enhance.jsx import React from 'react'; // 1 export let IntervalEnhance = ComponsedComponent => class extends React.Component { constructor(props) { super(props); this.state = { seconds: 0 // 2 }; } // 3 componentDidMount() { this.interval = setInterval(this.tick.bind(this), 1000); } // 3 componentWillUnmount() { clearInterval(this.interval); } tick() { this.setState({ seconds: this.state.seconds + 1000 }); } render() { // 4 return <ComponsedComponent {...this.props} {...this.state} />; } };
我们一一的来解释上面几处加注释的代码:
ComposedComponent => class extends React.Component
- 这其实和定义返回类的函数一样。 其中ComposedComponent
是我们想要“增强”的组件(在上面的代码中就是CartItem
组件)。 通过使用export let IntervalEnhance
我们可以导出整个函数为IntervalEnhance
(也就是上面代码中的CartItem
)。- 初始化组件的状态(state),设置
seconds
的值为0。 - 组件的生命周期钩子函数,用于期待能够和暂停计数器。
- 最重要的一个部分。这行代码将所有的
state
或props
拿到我们的“增强器”组件中,然后转移到CartItem
组件中。 通过这种方式,CartItem
组件将能够访问到this.state.seconds
属性。
最后一步是改变
CartItem
组件中的render
方法。我们将直接向视图中输出this.state.seconds
:import React from 'react'; import { IntervalEnhance } from "./interval-enhance.jsx"; class CartItem extends React.Component { // component code here render() { return <article className="row large-4"> <!-- some other tags here --> <p className="large-12 column"> <strong>Time elapsed for interval: </strong> {this.props.seconds} ms </p> </article>; } } export default IntervalEnhance(CartItem);
现在我们可以浏览器中检查结果了,我们将在页面中看到一行文字,显示用户在当前页面停留的时间。
注意:所有这一切并没有改变
CartItem
组件的任何主体代码(除了render
方法)! 这就是为什么高阶组件这么强大的原因!