add composables documentation

This commit is contained in:
Laila Los
2022-11-17 18:06:14 +01:00
parent 22e10c2340
commit 7e697b16d7
2 changed files with 116 additions and 6 deletions
+106
View File
@@ -0,0 +1,106 @@
# Composables
Composables are way of splitting up your code into distinct, reusable chunks.
They can replace providers, mixins and more. Any code you can put into a component, can also be written as a composable.
Using them effectively can make your code more reusable, decoupled, and easier to follow.
## Using Composables in the Composition API
Example: accessing the current user from the store
```vue
<script setup>
import { useCurrentUser } from "composables/user";
const { currentUser } = useCurrentUser();
</script>
```
You can now access the current user with `currentUser.value`.
## Using Composables in the Options API
Composables are not limited to the composition api. This is the same example from above, using the options api.
```vue
<script>
import { useCurrentUser } from "composables/user";
export default {
setup() {
const { currentUser } = useCurrentUser();
return { currentUser };
}
}
</script>
```
You can now access the current user with `this.currentUser` from anywhere within the component.
## Testing Components with Composable Stores
When writing a test which includes a component that has a composable store (like useCurrentUser),
there are two ways to test it.
### Mocking the store
You can provide the store in the mount function as follows:
```js
const wrapper = shallowMount(TestedComponent,
localVue,
provide: { store },
});
```
`store` must be a Vuex store.
The `mockModule` helper can help creating a store for the required modules:
```js
const store = new Vuex.Store({
modules: {
user: mockModule(userStore),
},
});
```
### Mocking the composable
The second option is to mock the composable:
```js
import { useCurrentUser } from "composables/user";
jest.mock("composables/user");
useCurrentUser.mockReturnValue({
currentUser: {}
});
```
While simpler in this example, you may need to manually mock more return values and composables than the other method, depending on the composables the component is using.
## Using Composables for more than Stores
Composables can be of great use to extract any reactive code from your components. For an example of this, take a look at [userFilterObjectArray](https://github.com/galaxyproject/galaxy/blob/dev/client/src/composables/utils/filter.js).
Usage:
```vue
<script setup>
import { useFilterObjectArray } from "composables/utils/filter";
const filteredArray = useFilterObjectArray(
someReactiveArray,
searchValue,
["name", "description"]
);
</script>
```
It's a simple filtering function, but fully reactive.
Whenever any of the inputs changes, the return value is re-computed, without having to call the function again.
## Further Reading
* [Composition API](https://vuejs.org/api/composition-api-setup.html)
* [\<script setup\>](https://vuejs.org/api/sfc-script-setup.html)
+10 -6
View File
@@ -1,3 +1,7 @@
**Notice** Consider using [Composables](composalbes.md) instead of Providers. They offer more functionality and need less boilerplate.
---
We are using components in two very distinct ways. The first, "normal", kind of component will
probably look familiar to anybody whis is already passingly familiar with Vue. Here the relevant
information comes in as properties, any internal variables get defined in "data", changes go out as
@@ -27,7 +31,7 @@ restriction that Vue needs a single root element in which to render.
<template>
<AutoComplete
:options="options"
:value="doodad.category"
:value="doodad.category"
@select="saveCategory"
/>
</template>
@@ -35,7 +39,7 @@ restriction that Vue needs a single root element in which to render.
<script>
export default {
props: {
props: {
doodad: { type: Object, required: true },
options: { type: Array, required: true },
},
@@ -51,7 +55,7 @@ export default {
This component accepts a mandatory input object (doodad), lets the user play with a category prop,
then emits a fresh object after it's done. So what, what's the big deal? The important part to walk
away from this dumb example is the things that are NOT in this sample component.
away from this dumb example is the things that are NOT in this sample component.
This component doesn't save the data. This component doesn't make ajax calls, and this component
doesn't mutate its props. What it does do is to allow the user to edit some object named "doodad"
@@ -149,8 +153,8 @@ describe("A renderless component", () => {
},
});
// waits for "updated" Vue lifecycle hook to fire on the renderless
// component. This is often good enough for waiting for
// waits for "updated" Vue lifecycle hook to fire on the renderless
// component. This is often good enough for waiting for
// an initial ajax load to finish, for example
await waitForLifecyleEvent(wrapper.vm, "updated");
})
@@ -161,4 +165,4 @@ describe("A renderless component", () => {
// ...more tests
})
})
```
```