Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Alternative pattern to component composition using forwarding technique #1671

Closed
509dave16 opened this issue Jul 21, 2020 · 2 comments
Closed

Comments

@509dave16
Copy link

509dave16 commented Jul 21, 2020

What problem does this feature solve?

This solves the problem of re-using common side effect or hydration logic for components. I haven't even used Vue for a week yet and quickly ran into the problem of trying to compose Higher Order Components around a Base Component. I was redirected to the Composition API RFC and npm package by a friend. But I noticed that it was very verbose and similar to using React Hooks. So I was hoping to use a more simpler solution to my problem.

What does the proposed API look like?

I decided to come up with my own pattern/solution to the problem I faced. I know this solution might be missing the forwarding of some options like $listeners, but it demonstrates a very simple way to compose HOCs around a base component.

Simple Pattern Example Demo: https://codesandbox.io/s/simple-vue-container-composition-sk7zb?fbclid=IwAR08Zck3exsxKV6iSxwSP5HLJqo-4nPTLvAb0YEd92r4iP8QIH7PR4ae_3w&file=/src/components/HelloWorld/index.js:0-296

Pattern overview:

  1. Forwarding HOC
  2. container HOF
  3. Simple Client Code Example
  4. Complex Client Code Demo

1. Forwarding HOC

The basic concept is that commonly used Forwarding HOC function receives the following two arguments:

  • BaseComponent
  • Mixin

The goal of this HOC is to use the following computed property from the Mixin to facilitate passing along additional props to the BaseComponent:

  • enhancingProps : a computed property that returns an object containing the additional props that should be passed along to the BaseComponent. This could also be a data property.

The Mixin itself should not mix in any props. This is to avoid props collisions. And because the BaseComponent's props are spread into the Forwarding component's props in order to continue the reactive chain.
EXAMPLE

export default function(BaseComponent, Mixin) {
  const propsSchema = BaseComponent.options
    ? BaseComponent.options.props
    : BaseComponent.props;
  return {
    mixins: [Mixin],
    inheritAttrs: false,
    name: "Forwarding",
    props: { ...propsSchema },
    components: { BaseComponent },
    render: function(createElement) {
      return createElement("BaseComponent", {
        props: { ...this.$props, ...(this.enhancingProps || {}) },
        attrs: { ...this.$attrs },
        scopedSlots: { ...this.$scopedSlots },
      });
    }
  }
}

2. container HOF

Now in order for someone to create their own custom HOC, they need to make a container which is simply just a Higher Order Function that receives any number of parameters for supporting the custom Mixin. And then the returned function receives the following parameter:

  • BaseComponent

The container HOF's returned function is responsible for calling the Forwarding HOC function passing the BaseComponent and a custom Mixin that will provide the computed property enhancingProps.
EXAMPLE

import Forwarding from "../Forwarding";

const Mixin = {
  data: function() {
    return {
      mixinProps: {
        hello: "hello"
      }
    };
  },
  computed: {
    enhancingProps: function() {
      return { ...this.mixinProps };
    }
  }
};

export default function() {
  return BaseComponent => Forwarding(BaseComponent, Mixin);
}

3. Simple Client Code Example

So in the client code a call to the container HOF from an index.js file of a component's folder could be made in order to compose the HOC around the BaseComponent. Example:

import withHello from "../containers/withHello";
import withWorld from "../containers/withWorld";
import HelloWorld from "./HelloWorld.vue";

const EnhancedComponent = withHello()(HelloWorld);
const DoubleEnhancedComponent = withWorld()(EnhancedComponent);
export default DoubleEnhancedComponent;

NOTE - That the BaseComponent which is in this case is HelloWorld, should declare the expected enhancingProps in it's props option in order for reactivity to occur if they were to change:

<template>
  <p>{{hello}} {{world}}</p>
</template>
<script>
export default {
  name: "HelloWorld",
  props: {
    hello: {
      type: String,
      default: ""
    },
    world: {
      type: String,
      default: ""
    }
  }
};
</script>

4. Complex Client Code Demo

A more complex example of this can be found here where I basically made a Vue version of the @nozbe/withObservables React HOC in order to hyrdate the root App component with blogs that are inserted into the WatermelonDB managed indexeddb database. NOTE: The Reset Database button doesn't reactively cause the listed blogs to disappear yet. Just focus on trying Generate 100 More button:
https://codesandbox.io/s/34cjw?file=/src/components/HelloWorld/index.js

This is loosely based on the demo code for WatermelonDB's web example which is in React: https://watermelondb.now.sh/

@yyx990803
Copy link
Member

yyx990803 commented Jul 21, 2020

In Vue 3 we strongly recommend against mixins and will not be investing more efforts into facilitating composition patterns that revolve around inheritance/mixins/HOCs. Of course, you are welcome to use whatever patterns you like, but you likely won't receive much feedback from us.

Composition API is designed exactly to solve component logic composition. I would recommend looking into it and try a bit harder beyond "it looks verbose" and "looks like React hooks" - neither are reasonable arguments to dismiss it outright.

Also - the repo issue list is for bugs and feature requests only, and this seems more like a general design discussion, which should be posted either to the forum, or to the RFCs repo.

@509dave16
Copy link
Author

509dave16 commented Jul 21, 2020

@yyx990803 Completely understand your situation with Vue 3. The same transition is happening in the React ecosystem to move away from HOCs and to hooks instead in a lot of packages. @apollo/client is a prime example where in version 3, HOCs are deprecated and will no longer be maintained. HOCs are just my preferred way to do component logic composition. I will definitely take a look at the Composition API again.

Thank you for letting me know where I should post these types of proposals! I was not certain as there was no option from the new issue builder for the RFCs repo or any mention that proposals should be made there: https://new-issue.vuejs.org

And thank you for the time that you took to briefly review and respond to this issue! I'll be sure to post in the correct place next time.

@github-actions github-actions bot locked and limited conversation to collaborators Nov 8, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants