Skip to content

dennybiasiolli/react-vuex

Repository files navigation

react-vuex

React bindings for Vuex inspired by react-redux project.

example workflow example workflow Codecov Status Coveralls Status

Donate with Liberapay

Installation

react-vuex requires React 16.0+, Vue 2.0+ and Vuex 3.0+

npm install --save react-vuex

This assumes that you’re using npm package manager with a module bundler like Webpack or Browserify to consume CommonJS modules.

Documentation

To look at some example projects, take a look at the examples section of this repo.

Example use

  • configure your store with state, getters, mutations and actions, according to Vuex documentation

    /*
     * actions.js
     */
    
    export const INCREMENT_ASYNC = 'INCREMENT_ASYNC';
    
    export default {
      incrementAsync: (value = 1) => ({
        type: INCREMENT_ASYNC,
        value,
      }),
    };
    
    
    /*
     * mutations.js
     */
    
    export const INCREMENT = 'INCREMENT';
    export const INCREMENT_START = 'INCREMENT_START';
    export const INCREMENT_STOP = 'INCREMENT_STOP';
    
    export default {
      increment: (value = 1) => ({
        type: INCREMENT,
        value,
      }),
    };
    
    
    /*
     * store.js
     */
    
    import Vue from 'vue';
    import Vuex from 'vuex';
    import { INCREMENT, INCREMENT_START, INCREMENT_STOP } from './mutations';
    import { INCREMENT_ASYNC } from './actions';
    
    Vue.use(Vuex);
    
    export default new Vuex.Store({
      state: {
        count: 0,
        isIncrementing: false,
      },
      getters: {
        countGreaterThan2: (state, getters) => state.count > 2,
      },
      mutations: {
        [INCREMENT](state) {
          state.count += 1;
        },
        [INCREMENT_START](state) {
          state.isIncrementing = true;
        },
        [INCREMENT_STOP](state) {
          state.isIncrementing = false;
        },
      },
      actions: {
        [INCREMENT_ASYNC]({ commit, state }, payload) {
          commit(INCREMENT_START);
          return new Promise((resolve) => {
            setTimeout(() => {
              commit(INCREMENT);
              resolve();
            }, 500);
          }).then(() => commit(INCREMENT_STOP))
            .then(() => state.count);
        },
      },
    });
  • use Provider in your app

    This will pass the store to all subcomponents of the app

    /*
     * index.js
     */
    
    import React from 'react';
    import { render } from 'react-dom';
    import { Provider } from 'react-vuex';
    import App from './components/App';
    import store from './store';
    
    render(
      <Provider store={store}>
        <App />
      </Provider>,
      document.getElementById('root'),
    );
  • create your container component mapping state, dispatch, commit and getter to the store

    /*
     * containers/MyContainer.js
     */
    
    import { connect } from 'react-vuex';
    import MyComponent from '../components/MyComponent';
    import mutations from '../mutations';
    import actions from '../actions';
    
    const mapStateToProps = (state, ownProps) => ({
      myCount: state.count,
    });
    const mapDispatchToProps = (dispatch, ownProps) => ({
      onIncrementAsync: val => dispatch(actions.incrementAsync(val)),
    });
    const mapCommitToProps = (commit, ownProps) => ({
      onIncrement: () => commit(mutations.increment()),
    });
    const mapGetterToProps = (getter, ownProps) => ({
      isGreaterThan2: getter.countGreaterThan2,
    });
    
    const MyContainer = connect(
      mapStateToProps,
      mapDispatchToProps,
      mapCommitToProps,
      mapGetterToProps,
    )(MyComponent);
    
    export default MyContainer;
  • create your presentational component using mapped props

    /*
     * components/MyComponent.js
     */
    
    import React from 'react';
    import PropTypes from 'prop-types';
    
    export default class MyComponent extends React.PureComponent {
      constructor(props, context) {
        super(props, context);
        this.handleInc = this.handleInc.bind(this);
        this.handleIncAsync = this.handleIncAsync.bind(this);
      }
      handleInc() {
        if (this.props.onIncrement) {
          this.props.onIncrement();
        }
      }
      handleIncAsync() {
        if (this.props.onIncrementAsync) {
          this.props.onIncrementAsync().then(() => {}));
        }
      }
      render() {
        return (
          <div>
            Count is {this.props.myCount !== undefined && `${this.props.myCount}, `}
            greater than 2: {this.props.isGreaterThan2 ? 'yes' : 'no'}
            {this.props.onIncrement &&
              <button onClick={this.handleInc}>Increment Sync</button>
            }
            {this.props.onIncrementAsync &&
              <button onClick={this.handleIncAsync}>Increment Async</button>
            }
          </div>
        );
      }
    }
    
    MyComponent.defaultProps = {
      children: undefined,
      isGreaterThan2: false,
      myCount: 0,
      onIncrement: undefined,
      onIncrementAsync: undefined,
    };
    
    MyComponent.propTypes = {
      children: PropTypes.node,
      isGreaterThan2: PropTypes.bool,
      myCount: PropTypes.number,
      onIncrement: PropTypes.func,
      onIncrementAsync: PropTypes.func,
    };
  • use your container component in app

    import React from 'react';
    import MyContainer from '../containers/MyContainer';
    
    export default class App extends React.Component {
      constructor(props, context) {
        super(props, context);
        this.state = {
          testValue: 123,
        };
      }
    
      render() {
        return (
          <div>
            <MyContainer />
          </div>
        );
      }
    }

License

MIT

Support on Beerpay or Liberapay

Hey dude! Help me out for a couple of 🍻!

Beerpay Beerpay

Donate with Liberapay