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> )专注于正确的原语