闽公网安备 35020302035485号
function ShoppingCart() {
this.items = [];
}
var orderMixin = {
calculateTotal() {
// 从 this.items 计算
}
// .. 其他方法
}
Object.assign(ShoppingCart.prototype, orderMixin)
var cart = new ShoppingCart()
cart.calculateTotal()
Javascript 不支持多重继承,因此 mixin 是重用共享行为和扩充类的一种方式。那该如何在使用 createClass 创建的组件之间共享逻辑呢?Mixins 是一种常用的模式,它可以访问组件的生命周期方法,允许我们组合逻辑、状态等:var SubscriptionMixin = {
getInitialState: function() {
return {
comments: DataSource.getComments()
};
},
// 堆代码 duidaima.com
// 当一个组件使用多个 mixin 时,React 会尝试合并多个mixin的生命周期方法,因此每个都会被调用
componentDidMount: function() {
console.log('do something on mount')
},
componentWillUnmount: function() {
console.log('do something on unmount')
},
}
// 将对象传递给 createClass
var CommentList = React.createClass({
// 在 mixins 属性下定义它们
mixins: [SubscriptionMixin, AnotherMixin, SomeOtherMixin],
render: function() {
var { comments, ...otherStuff } = this.state
return (
<div>
{comments.map(function(comment) {
return <Comment key={comment.id} comment={comment} />
})}
</div>
)
}
})
对于较小的应用,这种方式可以正常运行。但是,当 Mixin 应用到大型项目时,它们也有一些缺点:class MyComponent extends React.Component {
constructor(props) {
// 在组件挂载到 DOM 之前运行
// super 指的是父 Component 的构造函数
super(props)
}
componentWillMount() {}
componentDidMount(){}
componentWillUnmount() {}
componentWillUpdate() {}
shouldComponentUpdate() {}
componentWillReceiveProps() {}
getSnapshotBeforeUpdate() {}
componentDidUpdate() {}
render() {}
}
考虑到 mixin 的缺陷,我们该如何以这种编写 React 组件的新方式来共享逻辑和副作用呢?这时候,高阶组件 (HOC) 就出现了,它的名字来源于高阶函数的函数式编程概念。它成为了替代 Mixin 的一种流行方式,并出现在像 Redux 这样的库的 API 中,例如它的 connect 函数,用于将组件连接到 Redux 存储。除此之外,还有 React Router 的 withRouter。// 一个创建增强组件的函数,有一些额外的状态、行为或 props
const EnhancedComponent = myHoc(MyComponent);
// HOC 的简化示例
function myHoc(Component) {
return class extends React.Component {
componentDidMount() {
console.log('do stuff')
}
render() {
// 使用一些注入的 props 渲染原始组件
return <Component {...this.props} extraProps={42} />
}
}
}
高阶组件对于在多个组件之间共享通用行为非常有用。它们使包装的组件保持解耦和通用性,以便可以重用。然而,HOC 遇到了与 mixin 类似的问题:<Motion style={{ x: 10 }}>
{interpolatingStyle => <div style={interpolatingStyle} />}
</Motion>
主要思想就是将一个函数作为 props 传递给组件。然后组件会在内部调用该函数,并传递数据和方法,将控制反转回函数以继续渲染它们想要的内容。与 HOC 不同,组合发生在 JSX 内部的运行时,而不是静态模块范围内。它们没有名称冲突,因为很明确知道是从哪里来的,也更容易进行静态类型检查。<UserProvider>
{user => (
<UserPreferences user={user}>
{userPreferences => (
<Project user={user}>
{project => (
<IssueTracker project={project}>
{issues => (
<Notification user={user}>
{notifications => (
<TimeTracker user={user}>
{timeData => (
<TeamMembers project={project}>
{teamMembers => (
<RenderThangs renderItem={item => (
// ...
)}/>
)}
</TeamMembers>
)}
</TimeTracker>
)}
</Notification>
)}
</IssueTracker>
)}
</Project>
)}
</UserPreferences>
)}
</UserProvider>
这时,通常会将管理状态的组件与渲染 UI 的组件分开来处理。随着 Hooks 的出现,“容器”和“展示性”组件模式已经不再流行。但值得一提的是,这种模式在服务逇组件中有所复兴。目前,render props 仍然是创建可组合组件 API 的有效模式。function Example() {
const user = useUser();
const userPreferences = useUserPreferences(user);
const project = useProject(user);
const issues = useIssueTracker(project);
const notifications = useNotification(user);
const timeData = useTimeTracker(user);
const teamMembers = useTeamMembers(project);
return (
<div>
{/* 渲染内容 */}
</div>
);
}
权衡利弊2.函数则与函数式编程以及纯函数等概念有关联。
return (
<ImplementedWithMixins>
<ComponentUsingHOCs>
<ThisUsesHooks>
<ServerComponentWoah />
</ThisUsesHooks>
</ComponentUsingHOCs>
</ImplementedWithMixins>
)
专注于正确的原语