Skip to content

Commit 5d80796

Browse files
committed
update composables per feedback
1 parent 0b783d7 commit 5d80796

File tree

1 file changed

+44
-6
lines changed

1 file changed

+44
-6
lines changed

src/guide/reusability/composables.md

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,43 @@ const { x, y } = useMouse()
9090

9191
[Try it in the Playground](https://sfc.vuejs.org/#eyJBcHAudnVlIjoiPHNjcmlwdCBzZXR1cD5cbmltcG9ydCB7IHVzZU1vdXNlIH0gZnJvbSAnLi9tb3VzZS5qcydcblxuY29uc3QgeyB4LCB5IH0gPSB1c2VNb3VzZSgpXG48L3NjcmlwdD5cblxuPHRlbXBsYXRlPlxuICBNb3VzZSBwb3NpdGlvbiBpcyBhdDoge3sgeCB9fSwge3sgeSB9fVxuPC90ZW1wbGF0ZT4iLCJpbXBvcnQtbWFwLmpzb24iOiJ7XG4gIFwiaW1wb3J0c1wiOiB7XG4gICAgXCJ2dWVcIjogXCJodHRwczovL3NmYy52dWVqcy5vcmcvdnVlLnJ1bnRpbWUuZXNtLWJyb3dzZXIuanNcIlxuICB9XG59IiwibW91c2UuanMiOiJpbXBvcnQgeyByZWYsIG9uTW91bnRlZCwgb25Vbm1vdW50ZWQgfSBmcm9tICd2dWUnXG5cbmV4cG9ydCBmdW5jdGlvbiB1c2VNb3VzZSgpIHtcbiAgY29uc3QgeCA9IHJlZigwKVxuICBjb25zdCB5ID0gcmVmKDApXG5cbiAgZnVuY3Rpb24gdXBkYXRlKGV2ZW50KSB7XG4gICAgeC52YWx1ZSA9IGV2ZW50LnBhZ2VYXG4gICAgeS52YWx1ZSA9IGV2ZW50LnBhZ2VZXG4gIH1cblxuICBvbk1vdW50ZWQoKCkgPT4gd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ21vdXNlbW92ZScsIHVwZGF0ZSkpXG4gIG9uVW5tb3VudGVkKCgpID0+IHdpbmRvdy5yZW1vdmVFdmVudExpc3RlbmVyKCdtb3VzZW1vdmUnLCB1cGRhdGUpKVxuXG4gIHJldHVybiB7IHgsIHkgfVxufSJ9)
9292

93-
As we can see, the core logic remains exactly the same - all we had to do was moving it into an external function and return the state that should be exposed. The same `useMouse()` functionality can now be used in any component.
93+
As we can see, the core logic remains exactly the same - all we had to do was moving it into an external function and return the state that should be exposed. Same as inside a component, you can use the full range of [Composition API functions](/api/#composition-api) in composables. The same `useMouse()` functionality can now be used in any component.
9494

95-
You can use the full range of [Composition API functions](/api/#composition-api) in composables. You can also nest them: one composable function can call one or more other composable functions. This enables us to compose complex logic using small, isolated units, similar to how we compose an entire application using components. In fact, this is why we decided to call the collection of APIs that make this pattern possible Composition API.
95+
The cooler part about composables though, is that you can also nest them: one composable function can call one or more other composable functions. This enables us to compose complex logic using small, isolated units, similar to how we compose an entire application using components. In fact, this is why we decided to call the collection of APIs that make this pattern possible Composition API.
96+
97+
As an example, we can extract the logic of adding and cleaning up a DOM event listener into its own compsoable:
98+
99+
```js
100+
// event.js
101+
import { onMounted, onUnmounted } from 'vue'
102+
103+
export function useEventListener(target, event, callback) {
104+
// if you want, you can also make this
105+
// support selector strings as target
106+
onMounted(() => target.addEventListener(event, callback))
107+
onUnmounted(() => target.removeEventListener(event, callback))
108+
}
109+
```
110+
111+
And now our `useMouse()` can be simplified to:
112+
113+
```js{3,9-12}
114+
// mouse.js
115+
import { ref } from 'vue'
116+
import { useEventListener } from './event'
117+
118+
export function useMouse() {
119+
const x = ref(0)
120+
const y = ref(0)
121+
122+
useEventListener(window, 'mousemove', (event) => {
123+
x.value = event.pageX
124+
y.value = event.pageY
125+
})
126+
127+
return { x, y }
128+
}
129+
```
96130

97131
:::tip
98132
Each component instance calling `useMouse()` will create its own copies of `x` and `y` state so they won't interfere with one another. If you want to manage shared state between components, read the [State Management](/guide/scaling-up/state-management.html) chapter.
@@ -117,7 +151,10 @@ fetch('...')
117151
118152
<template>
119153
<div v-if="error">Oops! Error encountered: {{ error.message }}</div>
120-
<div v-else-if="data">Data loaded: <pre>{{ data }}</pre></div>
154+
<div v-else-if="data">
155+
Data loaded:
156+
<pre>{{ data }}</pre>
157+
</div>
121158
<div v-else>Loading...</div>
122159
</template>
123160
```
@@ -197,7 +234,7 @@ It is a convention to name composable functions with camelCase names that start
197234

198235
### Input Arguments
199236

200-
A composable can accept ref arguments even if it doesn't rely on it for reactivity. If you are writing a composable that may be used by other developers, it's a good idea to handle the case of input arguments being refs instead of raw values. The [`unref()`](/api/reactivity-utilities.html#unref) utility function will come handy for this purpose:
237+
A composable can accept ref arguments even if it doesn't rely on it for reactivity. If you are writing a composable that may be used by other developers, it's a good idea to handle the case of input arguments being refs instead of raw values. The [`unref()`](/api/reactivity-utilities.html#unref) utility function will come in handy for this purpose:
201238

202239
```js
203240
import { unref } from 'vue'
@@ -209,7 +246,7 @@ function useFeature(maybeRef) {
209246
}
210247
```
211248

212-
If your composable expects to setup a `watchEffect()` using the input ref, make sure to call `unref()` inside the effect callback so it's tracked as a dependency.
249+
If your composable creates reactive effects when the input is a ref, make sure to either explicitly watch the ref with `watch()`, or call `unref()` inside a `watchEffect()` so that it is propertly tracked.
213250

214251
### Return Values
215252

@@ -229,6 +266,7 @@ const mouse = reactive(useMouse())
229266
// mouse.x is linked to original ref
230267
console.log(mouse.x)
231268
```
269+
232270
```vue-html
233271
Mouse position is at: {{ mouse.x }}, {{ mouse.y }}
234272
```
@@ -239,7 +277,7 @@ It is OK to perform side effects (e.g. adding DOM event listeners or fetching da
239277

240278
- If your are working in an application that utilizes [Server-Side Rendering](/guide/advanced/server-side-rendering.html) (SSR), make sure to perform DOM-specific side effects in post-mount lifecycle hooks, e.g. `onMounted()`. These hooks are only called in the browser so you can ensure code inside it has access to the DOM.
241279

242-
- Make sure to clean up side effects in `onUnmounted()`. For example, if a composable sets up a DOM event listener, it should remove that listener in `onUnmounted()` (as we have seen in the `useMouse()` example). It can also be a good idea to use [a composable that automatically does this for you](https://vueuse.org/core/useeventlistener/).
280+
- Make sure to clean up side effects in `onUnmounted()`. For example, if a composable sets up a DOM event listener, it should remove that listener in `onUnmounted()` (as we have seen in the `useMouse()` example). It can also be a good idea to use a composable that automatically does this for you, like the `useEventListener()` example.
243281

244282
### Usage Restrictions
245283

0 commit comments

Comments
 (0)