1 | # vue-hoc
|
2 | Create Higher Order Vue Components
|
3 |
|
4 | Inspired by https://github.com/vuejs/vue/issues/6201
|
5 |
|
6 | ## Installation
|
7 | ```
|
8 | npm install --save vue-hoc
|
9 | ```
|
10 |
|
11 | ## Usage
|
12 | The simplest way to create a Higher Order Component is with the `createHOC` method. It takes a base component, a set of component options to apply to the HOC, and a set of *data* properties to pass to the component during render.
|
13 | ```js
|
14 | import { createHOC } from 'vue-hoc';
|
15 | import MyComponent from '../my-component';
|
16 |
|
17 | const options = {
|
18 | name: 'MyEnhancedComponent',
|
19 | computed: {
|
20 | myComputedProperty(){
|
21 | return this.someProp + ' computed';
|
22 | }
|
23 | },
|
24 | created(){
|
25 | console.log('Created')
|
26 | }
|
27 | };
|
28 |
|
29 | const renderWith = {
|
30 | props: {
|
31 | someProp(){
|
32 | return this.myComputedProperty;
|
33 | }
|
34 | },
|
35 | listeners: {
|
36 | someEvent(arg){
|
37 | this.$emit('someOtherEvent', arg);
|
38 | }
|
39 | }
|
40 | };
|
41 |
|
42 | const enhanced = createHOC(MyComponent, options, renderWith);
|
43 | ```
|
44 | The resulting HOC component will render the base component, but will pass in the value of `myComputedProperty` in place of `someProp`.
|
45 |
|
46 | The alt method `createHOCc` exposes a curried version of the same method, where the component is the last argument, allowing you to write *HOC creators* and potentially chain up multiple hocs:
|
47 | ```js
|
48 | import { createHOCc } from 'vue-hoc';
|
49 | import { compose } from 'ramda';
|
50 | import MyComponent from '../my-component';
|
51 |
|
52 | const withCreatedHook = createHOCc({
|
53 | created(){
|
54 | console.log('Created');
|
55 | }
|
56 | }, null);
|
57 |
|
58 | const withAmendedProp = createHOCc(null, {
|
59 | props: {
|
60 | someProp(){
|
61 | return this.someProp + ' amended';
|
62 | }
|
63 | }
|
64 | });
|
65 |
|
66 | // we can now create a HOC using these methods
|
67 | const MyComponent2 = withCreatedHook(MyComponent);
|
68 |
|
69 | // and we can do multiple hocs:
|
70 | const MyComponent3 = withAmendedProp(withCreatedHook(MyComponent));
|
71 |
|
72 | // and with a composer like ramda's compose, we can make it more readable:
|
73 | const enhance = compose(
|
74 | withAmendedProp,
|
75 | withCreatedHook
|
76 | );
|
77 | const MyComponent4 = enhance(MyComponent);
|
78 | ```
|
79 |
|
80 | ## API
|
81 | ### createHOC
|
82 | ```js
|
83 | (Component: Object | Function, options?: Object, renderWith?: Object) => Object;
|
84 | ```
|
85 | Wraps a component in a higher order component. Any props, listeners, and attributes will be passed through the HOC into the original Component.
|
86 | ```js
|
87 | const hocComponent = createHOC(Component);
|
88 | ```
|
89 |
|
90 | #### options
|
91 | The options object will be used as the HOC's component definition. Here you can pass any valid [component definition options](https://vuejs.org/v2/api/#Options-Data).
|
92 | ```js
|
93 | const withCreatedHook = createHOC(Component, {
|
94 | created(){
|
95 | console.log(this.someProp);
|
96 | // Where some prop is a prop defined on the original component.
|
97 | // The HOC will have access to it and it will still be passed on to the original component.
|
98 | }
|
99 | });
|
100 | ```
|
101 | **vue-hoc** will automatically inherit the base component's props so you can access these from within the hoc and they will be passed into the base component during the render. If you set a new value for props, it will be merged with the inherited props using Vue's [option merging strategies](https://vuejs.org/v2/api/#optionMergeStrategies).
|
102 | ```js
|
103 | createHOC(Component, {
|
104 | props: ['someAdditionalProp']
|
105 | });
|
106 | ```
|
107 |
|
108 | **vue-hoc** will also automatically create a render function for the HOC, but you can override this by setting a `render` function yourself. Keep in mind, however, that a custom render function will no longer handle the `renderWith` options.
|
109 | ```js
|
110 | createHOC(Component, {
|
111 | render(h){
|
112 | /* ... */
|
113 | }
|
114 | });
|
115 | ```
|
116 |
|
117 | #### renderWith
|
118 | The renderWith object allows you to amend what props, listeners and attributes will be passed into the child component. In actuality, you can pass in any property that is accepted by Vue's [createElement](https://vuejs.org/v2/guide/render-function.html#The-Data-Object-In-Depth) method.
|
119 |
|
120 | >The exception is that the `on` property is renamed to `listeners`.
|
121 |
|
122 | Each option can be one of the following formats:
|
123 | ```js
|
124 | {
|
125 | [name: string]: any
|
126 | }
|
127 | ```
|
128 | This will just pass static properties into the component instance. i.e.
|
129 | ```js
|
130 | createHOC(Component, null, {
|
131 | props: {
|
132 | staticProp: 'foo',
|
133 | otherStaticProp: [1, 2, 3]
|
134 | }
|
135 | });
|
136 | ```
|
137 | The properties will be merged into the existing properties.
|
138 |
|
139 | ```js
|
140 | {
|
141 | [name: string]: (owner: Object) => any
|
142 | }
|
143 | ```
|
144 | This allows you to calculate specific properties individually. You can also include static properties alongisde this. i.e.
|
145 | ```js
|
146 | createHOC(Component, null, {
|
147 | props: {
|
148 | dynamicProp(props){
|
149 | return props.someProp + ' dynamic';
|
150 | },
|
151 | otherDynamicProp(){
|
152 | return this.someOtherProp + ' dynamic';
|
153 | },
|
154 | staticProp: 'foo'
|
155 | }
|
156 | });
|
157 | ```
|
158 | The properties will be merged into the existing properties.
|
159 |
|
160 | Keep in mind that `listeners`, `nativeOn`, and `scopedSlots` are meant to be functions so they will not be evaluated.
|
161 |
|
162 | ```js
|
163 | (owner: Object) => any
|
164 | ```
|
165 | This allows to return the entire property object. i.e.
|
166 | ```js
|
167 | createHOC(Component, null, {
|
168 | props(props){
|
169 | return {
|
170 | ...props,
|
171 | dynamicProp: 'dynamic'
|
172 | };
|
173 | }
|
174 | });
|
175 | ```
|
176 | Unlike the previous variants, this will *not* automatically merge with the existing properties.
|
177 |
|
178 | ### createHOCc
|
179 | ```js
|
180 | (options: Object, renderWith: Object, Component: Object | Function) => Object;
|
181 | ```
|
182 | This is a curried variation of the `createHOC` method. This allows you to build a HOC creator and pass in a component at the end.
|
183 |
|
184 | ### createRenderFn
|
185 | ```js
|
186 | (Component: Object, renderWith?: Object)=> Function;
|
187 | ```
|
188 | createRenderFn is responsible for rendering the wrapped component in your hoc.
|
189 | ```js
|
190 | const hoc = createHOC(Component, {
|
191 | render: createRenderFn(Component, {})
|
192 | });
|
193 | ```
|
194 | It is already used by `createHOC` to generate the render property of the component so you do not need to pass it in every time.
|
195 |
|
196 | #### options
|
197 | See [renderWith](#renderwith).
|
198 |
|
199 | ### createRenderFnc
|
200 | ```js
|
201 | (options: Object, Component: Object)=> Function;
|
202 | ```
|
203 | A curried version of `createRenderFn`.
|
204 |
|
205 | ### normalizeSlots
|
206 | ```js
|
207 | (slots: Object) => Array<Object>;
|
208 | ```
|
209 | A simple method that takes a component's slots and converts them into an array. This is used to pass distributed content from a parent to a child component during the render.
|