Skip to content

Commit 471bfed

Browse files
author
Declan de Wet
authored
Merge pull request #18 from declandewet/watcher-logic
circumvent need to call refresh() after async actions
2 parents c20206d + 1c9f6d0 commit 471bfed

File tree

5 files changed

+53
-41
lines changed

5 files changed

+53
-41
lines changed

README.md

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -681,38 +681,28 @@ Easy. Instead of defining `metaInfo` as an object, define it as a function and a
681681

682682
## How do I populate `metaInfo` from the result of an asynchronous action?
683683

684-
`vue-meta` exposes a method called `refresh` on the client-side that allows you to trigger an update at any given point in time.
684+
`vue-meta` will do this for you automatically when your component state changes.
685685

686-
In the same way you access `$meta().inject()` on the server, you can access `$meta().refresh()`.
687-
688-
For example, if you're using Vuex and you have an action that fetches a `post` asynchronously, you should ensure that it returns a promise so that you are notified when the fetching is complete:
686+
Just make sure that you're using the function form of `metaInfo`:
689687

690688
```js
691689
{
692-
actions: {
693-
async fetchPost ({ commit }, payload) {
694-
const post = yield db.fetch('posts', payload.postId)
695-
commit('fetchedPost', post)
690+
data () {
691+
return {
692+
title: 'Foo Bar Baz'
696693
}
697694
}
698-
}
699-
```
700-
701-
Then in your component, you can call `refresh()` to trigger an update once the fetch is complete:
702-
703-
```js
704-
{
705-
beforeMount () {
706-
const postId = this.$router.params.id
707-
this.$store.dispatch('fetchPost', { postId })
708-
.then(() => this.$meta().refresh())
695+
metaInfo () {
696+
return {
697+
title: this.title
698+
}
709699
}
710700
}
711701
```
712702

713-
Just make sure that whatever data source you're using (`store` if you're using Vuex, component `data` otherwise) has some sane defaults set so Vue doesn't complain about `null` property accessors.
703+
Check out the [vuex-async](https://github.com/declandewet/vue-meta/tree/master/examples/vuex-async) example for a far more detailed demonstration if you have doubts.
714704

715-
Check out the [vuex-async](https://github.com/declandewet/vue-meta/tree/master/examples/vuex-async) example for a far more detailed demonstration of how this works.
705+
Credit & Thanks for this feature goes to [Sébastien Chopin](https://github.com/Atinux).
716706

717707
# Examples
718708

examples/vuex-async/store.js

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -63,15 +63,10 @@ export default new Vuex.Store({
6363
actions: {
6464
getPost ({ commit }, payload) {
6565
commit('loadingState', { isLoading: true })
66-
// we have to return a promise from this action so we know
67-
// when it is finished
68-
return new Promise((resolve) => {
69-
setTimeout(() => {
70-
commit('getPost', payload)
71-
resolve()
72-
}, 2000)
73-
})
74-
.then(() => commit('loadingState', { isLoading: false }))
66+
setTimeout(() => {
67+
commit('getPost', payload)
68+
commit('loadingState', { isLoading: false })
69+
}, 2000)
7570
}
7671
}
7772
})

examples/vuex-async/views/Post.vue

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,7 @@
1818
name: 'post',
1919
beforeMount () {
2020
const { slug } = this.$route.params
21-
// since fetching a post is asynchronous,
22-
// we need to call `this.$meta().refresh()`
23-
// to update the meta info
2421
this.$store.dispatch('getPost', { slug })
25-
.then(() => this.$meta().refresh())
2622
},
2723
computed: mapGetters([
2824
'isLoading',

src/client/batchUpdate.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* Performs a batched update. Uses requestAnimationFrame to prevent
3+
* calling a function too many times in quick succession.
4+
* You need to pass it an ID (which can initially be `null`),
5+
* but be sure to overwrite that ID with the return value of batchUpdate.
6+
*
7+
* @param {(null|Number)} id - the ID of this update
8+
* @param {Function} callback - the update to perform
9+
* @return {Number} id - a new ID
10+
*/
11+
export default function batchUpdate (id, callback) {
12+
window.cancelAnimationFrame(id)
13+
return window.requestAnimationFrame(() => {
14+
id = null
15+
callback()
16+
})
17+
}

src/shared/plugin.js

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import assign from 'object-assign'
22
import $meta from './$meta'
3+
import batchUpdate from '../client/batchUpdate'
34

45
import {
56
VUE_META_KEY_NAME,
@@ -33,18 +34,31 @@ export default function VueMeta (Vue, options = {}) {
3334
Vue.prototype.$meta = $meta(options)
3435

3536
// store an id to keep track of DOM updates
36-
let requestId = null
37+
let batchID = null
3738

3839
// watch for client side component updates
3940
Vue.mixin({
41+
beforeCreate () {
42+
// coerce function-style metaInfo to a computed prop so we can observe
43+
// it on creation
44+
if (typeof this.$options[options.keyName] === 'function') {
45+
this.$options.computed.$metaInfo = this.$options[options.keyName]
46+
}
47+
},
48+
created () {
49+
// if computed $metaInfo exists, watch it for updates & trigger a refresh
50+
// when it changes (i.e. automatically handle async actions that affect metaInfo)
51+
// credit for this suggestion goes to [Sébastien Chopin](https://github.com/Atinux)
52+
if (this.$metaInfo) {
53+
this.$watch('$metaInfo', () => {
54+
// batch potential DOM updates to prevent extraneous re-rendering
55+
batchID = batchUpdate(batchID, () => this.$meta().refresh())
56+
})
57+
}
58+
},
4059
beforeMount () {
4160
// batch potential DOM updates to prevent extraneous re-rendering
42-
window.cancelAnimationFrame(requestId)
43-
44-
requestId = window.requestAnimationFrame(() => {
45-
requestId = null
46-
this.$meta().refresh()
47-
})
61+
batchID = batchUpdate(batchID, () => this.$meta().refresh())
4862
}
4963
})
5064
}

0 commit comments

Comments
 (0)