**Notice** Consider using [Composables](composables.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
events.
### Composition Example
```html static
```
In this example, we've created a component whose job is to deal with loading and updating the doodad
object. Notice that there is no markup inside the DoodadProvider other than the explicit renderless
component we previously made, but you are free to putput whatever you want in there, accessing the
doodad and saveDoddad properties as desired, as well as any other local data with the only
restriction that Vue needs a single root element in which to render.
## The Renderer
```html static
```
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.
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"
and emits a new fresh version of that doodad when it's done. (Note also that we are using the
update:propname event syntax whenever possible [to facilitate .sync
binds](https://vuejs.org/v2/guide/components-custom-events.html#sync-Modifier)).
Whatever happens to that new object is somebody else's job. As soon as you tie the data management
to the rendering, the re-usability of your components craters.
## The Provider
As the opposite of the rendering component, a provider or renderless component, is pure logic. It
should not know or care what your renderer is going to do with the data it provides. It is simply a
fancy way of configuring some data manipulation methods. This is one of the many ways of reusing
functionality available in Vue. Some others are [Mixins](https://vuejs.org/v2/guide/mixins.html),
[Provide/Inject](https://v3.vuejs.org/guide/component-provide-inject.html) and (in Vue3) [the
composition API](https://v3.vuejs.org/guide/composition-api-introduction.html).
```js static
// DoodadProvider.js
import { loadDoodad, saveDoodad } from "./someAjaxQueryModule";
export default {
data() {
return {
loaded: false,
doodad: null
}
},
methods: {
async saveDoodad(newVal) {
this.loaded = false;
const newVal = await saveDoodad(newVal);
this.doodad = newVal;
this.loaded = true;
},
async loadDoodad(newVal) {
this.loaded = false;
const newVal = await loadDoodad(newVal);
this.loaded = true;
return newVal;
}
},
async created() {
this.doodad = await loadDoodad();
},
render() {
return return this.$scopedSlots.default({
loaded: this.loaded,
doodad: this.doodad,
saveDoodad: this.saveDoodad
});
}
}
```
Here is an example of a simple possible renderless provider. The important part about this is the
render() function which simply renders [one big default
slot](https://vuejs.org/v2/guide/components-slots.html) and binds some of its own properties to that
slot for use by downstream components.
## Unit Testing a renderless component
Testing of a component like our editor is pretty straightforward and follows the standard Vue
guidelines. If you did a clean-enough job you won't even need to mock anything since all your data
dependencies should be delivered via props.
But it's not so obvious how to unit-test a renderless provider. What do you check for? There's no
mandatory markup, just one big empty slot.
```js static
// Testing a renderless component
import { shallowMount } from "@vue/test-utils";
import { getLocalVue, waitForLifecyleEvent } from "@tests/vitest/helpers";
import DoodadProvider from "./DoodadProvider";
describe("A renderless component", () => {
const localVue = getLocalVue();
let wrapper;
let slotProps;
beforeEach(async () => {
wrapper = shallowMount(DoodadProvider, {
localVue,
// The mount fn allows you to hook into a slot for this very reason
scopedSlots: {
default(props) {
slotProps = props;
},
},
});
// 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");
});
test("someProp", () => {
const { someProp } = slotProps;
expect(someProp).toExist();
// ...more tests
});
});
```