posva / vuexfire

Check
https://github.com/vuejs/vuefire
MIT License
558 stars 49 forks source link

Question: effective 2-way binding between vuex store and firebase #61

Closed dsl101 closed 7 years ago

dsl101 commented 7 years ago

I've got the basic setup working in that the state is bound to a firebase reference, and changes to the firebase are immediately reflected in the app.

I would like the app (components) to not really know anything about firebase though, and I had originally assumed that changing the state bound to firebase would also trigger updates to firebase. But I guess not - looking at the source it looks like it's only listening for firebase updates, not also making firebase writes.

Do you have plans to make vuexfire work in both directions?

If not, I guess I would need to write to firebase in the store actions - is that a good (or best) place to do it? Is there an existing pattern for replicating state changes back to firebase?

Tx

posva commented 7 years ago

I thought about it and I'll implement it and play around to see if it's worth. If I ever do it and it works, I'll probably do it on vuefire too.

Yes, you should do the firebase writes in actions. Not that I know about. It really depends on what you need to write. That part is documented in the Firebase docs, which is sometimes messy but they really have a lot of resources to learn. I definitely recommend you their youtube channel. Their videos are pretty cool :+1:

dsl101 commented 7 years ago

Thanks - I'll start working on it and post anything I learn that might be useful here. What I'm really after is a kind of 'online' version of a vuex store - or perhaps part of a store, or a module within a store. So that the main app and components can simply deal with the store as they would usually, and know that it's always in sync with any other clients. I guess not dissimilar to couch/pouch...

posva commented 7 years ago

We probably have the same thing in mind 🙂 Basically, doing automatically ref.child('somePath').set(value) when doing boundRef.somePath = value. But you'll still need to handle manually simultaneous updates and others

dsl101 commented 7 years ago

Yes. Although I hit the first roadblock quickly! Once bindFirebaseRef() has been called, it doesn't seem possible to retrieve that fb ref again in relation to the vuex object that's being updated via an action. In the action, what currently says:

setName (context, newName) {
    context.commit('setName', newName)
}

should probably say something like:

setName (context, newName) {
    context.firebaseRefFor(state.user).update(newName)
    .then(  // Handle success)
    .catch(  // Handle error)
}

On success, the existing vuexfire mechanism would see the firebase change and update the local actual state. On error, the action can either handle it or return an error to the component.

I can also see how an optimistic local commit() could be done here to change the local app state - the vuexfire update later wouldn't actually change anything, and the action could set the local state back on error.

But, how to implement firebaseRefFor(boundStateProperty)? At the moment, all I can see is storing a separate lookup table in the state, which doesn't seem very elegant. Does / can vuexfire expose that data in the context object somehow?

posva commented 7 years ago

It can but I don't think it's necessary because you can hold it in a variable:

// actions.js
const keyRef = null

export const action = firebaseAction(({ bindFirebaseRef }, { ref }) => {
  bindFirebaseRef('key', ref)
  keyRef = ref
})

Then simply use it in any other action. I'll think about it for tests

dsl101 commented 7 years ago

Yes, that's what I was hoping not to do - if there's only 1 ref, then it's fine. But I think I will have several different parts of the state mapped to different fb refs, and each having it's own variable would get a bit clumsy. But obviously it's quick and easy to start with... Maybe I could add state.user._fbRef as a convention.

posva commented 7 years ago

I don't think you should hold the refs on the state because it doesn't belong there. But you can use a WeakMap or something similar to hold every ref in a single dict

dsl101 commented 7 years ago

I guess there's no point having vuex deal with them as part of the replicated state, no. I see in the vuexfire source you use commit as the weakmap key for the reference - is that the way to go? I also wondered about just using an array and the vuexfire-created .key property as the index - are those keys unique?