titouancreach / vuejs-redux

Flexible binding between Vue and Redux
57 stars 11 forks source link

Why don't support connect? #20

Closed zhangchen91 closed 5 years ago

zhangchen91 commented 6 years ago

I think in vue's pattern, we can use provide/inject to support Provider and Connect Components, and then It will be more redux-liked?

For example:

Provider:

<script>
export default {
  name: 'Provider',

  props: {
    store: {
      required: true,
      type: Object,
      validator: function (store) {
        if (!store.dispatch && !store.subscribe && !store.getState) {
          throw new Error('[revux] - store provided is not a valid redux store')
        }
        return true
      }
    }
  },

  provide () { // Provided properties are also not reactive
    return {
      $$store: this.store
    }
  },

  render (h) {
    if (this.$slots.default.length > 1) {
      return h('div', this.$slots.default)
    }
    return this.$slots.default[0]
  }
}
</script>

Connect:

<script>
export default {
  name: 'Connect',

  props: {
    mapDispatchToProps: {
      required: false,
      default: () => ({}),
      type: Function
    },

    mapStateToProps: {
      required: false,
      default: () => ({}),
      type: Function
    }
  },

  inject: ['$$store'],

  created () {
    this._unsubscribe = this.$$store.subscribe(() => {
      this.$forceUpdate() // I think there's no need for the Data Methods
    })
  },

  destroyed () {
    this._unsubscribe()
  },

  render () {
    const {
      $$store,
      $scopedSlots,
      mapStateToProps,
      mapDispatchToProps
    } = this
    const nodes = $scopedSlots.default({
      ...mapDispatchToProps($$store.dispatch),
      ...mapStateToProps($$store.getState())
    })
    if (Array.isArray(nodes)) {
      return nodes[0]
    } else {
      return nodes
    }
  }
}
</script>

and the use case may like this

App.vue:

<template>
  <Provider id="app" :store="store">
    <router-view/>
  </Provider>
</template>

<script>
  import Provider from './components/Provider.vue'
  import store from "./rematch"
  export default {
    name: 'app',
    components: {
      Provider
    },
    data: () => ({
      store
    })
  }
</script>

Counter.vue:

<template>
  <Connect
    :mapStateToProps="mapStateToProps"
    :mapDispatchToProps="mapDispatchToProps">
    <Count slot-scope="props"
      :count="props.count"
      :increment="props.increment"
      :incrementAsync="props.incrementAsync" />
  </Connect>
</template>

<script>
import Connect from '../components/Connect.vue'
import Count from "../components/Count.vue"

const mapStateToProps = state => ({
  count: state.counter
})

const mapDispatchToProps = dispatch => ({
  increment: dispatch.counter.increment,
  incrementAsync: dispatch.counter.incrementAsync
})

export default {
  components: {
    Connect,
    Count
  },

  methods: {
    mapStateToProps,
    mapDispatchToProps
  }
}
</script>

inspired: https://github.com/edvincandon/revux related: https://vuejs.org/v2/guide/components-edge-cases.html#Dependency-Injection demo: https://github.com/zhangchen91/vue-rematch-immer

titouancreach commented 6 years ago

Hello! That looks good at first glance! I didn't do that because in my primary use case, I needed multiple stores, so I couldn't rely of one provided for my entire app.

Nowaday, I don't really work much with Vuejs but I will be very happy to accept any pull request! Feel free to contribute :)

titouancreach commented 6 years ago

Maybe we should export multiple named component like:

titouancreach commented 6 years ago

@zhangchen91 ping, are you insterested in contributing ?

titouancreach commented 5 years ago

I close because it's old, reopen if you want to contribute