Revux
Revux copied to clipboard
Bind dispatch on events
Hello Edvin,
Que pense tu de cabler le dispatch sur les events d'un component ? Avec ce patch on a les deux: binding sur les events, et injection des actions en data... chacun choisi sa religion ;)
Coverage remained the same at 100.0% when pulling 07fa5646166a10d2f8d5d8e102b6a5aa2e11b8cc on LeMisterV:dispatch-events into c1bfd0f15442b07b7e9f4a6a6edb4016b2ff974b on edvincandon:master.
Coverage remained the same at 100.0% when pulling 93e6f2eed99e9f567ebfae0377d028f9113755a0 on LeMisterV:dispatch-events into c1bfd0f15442b07b7e9f4a6a6edb4016b2ff974b on edvincandon:master.
Hey thx :) Le use-case ce serait pour catcher au passage l'event émis dans un composant parent?
Du coup je pense qu'on pourrait simplifier et passer par une option dans la fonction connect()
Tu penses quoi de faire ça :
Ne faire le bindActionCreators
qu'une seule fois :
let actionCreators
return {
// ...
data () {
actionCreators = bindActionCreators(mapDispatch, this.$$store.dispatch)
// ...
}
Du coup en gros on pourrait mapper directement le mapDispatch sur les events en passant l'option
{ mapToEvents: true }
- je vois pas trop de raison pour séparer les actionCreators dans deux objects distincts.
et en suite dans le created()
:
if (options.mapToEvents) {
Object.keys(actionCreators).forEach(key => vm.$on(key, actionCreators[key]))
// plus besoin du forEach custom
}
Du coup le connect final sur un composant ressemblerait à ça:
connect(mapState, mapDispatch, { mapToEvents: true })
Bout à bout:
connector.js
:
const connector = (_mapState = defaultMapState, mapDispatch = defaultMapDispatch, options = { mapToEvents: false }) => component => {
const mapState = normalizeMapState(_mapState)
let actionCreators
return {
name: `connect-${component.name}`,
mixins: [component],
inject: ['$$store'],
data () {
actionCreators = bindActionCreators(mapDispatch, this.$$store.dispatch)
const initData = {}
const mapData = {
...mapState(this.$$store.getState()),
...actionCreators
}
Object.keys(mapData).forEach(key => {
initData[key] = mapData[key]
})
return initData
},
created () {
const vm = this
if (options.mapToEvents) {
Object.keys(actionCreators).forEach(key => vm.$on(key, actionCreators[key]))
}
const getMappedState = state => mapState(state)
const observeStore = (store, select, onChange) => {
let currentState = select(store.getState())
function handleChange() {
const nextState = select(store.getState())
if (!shallowEqual(currentState, nextState)) {
const previousState = currentState
currentState = nextState
onChange(currentState, previousState)
}
}
return store.subscribe(handleChange)
}
this._unsubscribe = observeStore(this.$$store, getMappedState, (newState, oldState) => {
Object.keys(newState).forEach(key => {
vm.$set(this, key, newState[key])
})
})
},
beforeDestroy() {
this._unsubscribe()
}
}
}
Also: je viens de modifier quelques outils de dev sur master
Je use-case, c'est plutôt de binder une action redux soit dans un data comme c'était fait avant, soit sur un event émis par le component. Suivant le comportement du composant, ça peut rendre le code plus simple/claire/cohérent de cabler les actions redux sur les events mais ça peut laisser plus de liberté de pouvoir cabler sur des data. Donc avoir les deux solutions me semble intéressant.
Mais ça ne me semble pas pertinent d'avoir les deux à la fois. systématiquement.
Je vois - t'aurais un exemple de use-case ou le fait de mapper les actions sur les events du composant aurait une autre utilité que l'économie de faire manuellement des:
this.$on('action', this.doAction)
dans le created()
où this.doAction
est un action creator?
Justement, quand tu fais : this.$on('action', this.doAction)
Tu appel dans ton code une fonction (this.doAction) que tu ne déclare pas au sein du composant, donc ton composant n'est pas autonome. Si tu bind direct ton action sur l'évent, tu n'as plus à utiliser une méthode non déclarée. C'est plus propre, notamment quand tu fais d'un côté le composant et de l'autre le connecteur. Je te fais un petit use-case dès que je suis sur mon pc 😉
@LeMisterV ping pour avoir des news ;)
Voila un petit exemple d'usage de revux avec mapDispatch et mapDispatchEvent. https://gist.github.com/LeMisterV/abd13b649279f09ce5f8d12cd4f8c145
Le composant contient un bouton qui émet un event "action" sur lequel on veut connecté une action redux. Et on souhaite dispatch aussi une action sur le destroy. Deux conditions de connexion à redux légèrement différentes. Pour le bouton, la connexion a redux est plus naturellement faite en utilisant l'événement. Pour le hook lifecycle beforeDestroy, utiliser une connexion par data semble plus logique, on ne souhaite pas exposer un event sur ce hook, ça n'aurait pas de sens pour le composant.
La séparation composant / connecteur permet de plus facilement tester chaque partie