Vuex State and Getters (2)

Vuex State and Getters (2)

To be honest, I wasn't sure what I was writing when I wrote the previous article about Vuex State and Getters. And the more I use Vuex, the more I question this "everything in Getters" proposition. In the end, writing this article allowed me to review the situation.

I know why something did not smell right to me.

The case of the Vuejs components

Several data are accesible within a component, they can come from props, be defined in data or be calcultated in computed.

When we use them in the teplate, we don't ask ourselves the question of their origins, they are all accessible through the this interface.

Vue.component("ButtonCounter", {
  props: {
    name: {
      type: String,
      default: () => "John Doe"
    }
  },
  data: function() {
    return {
      count: 0
    };
  },
  computed: {
    times: function() {
      return this.count === 1 ? "time" : "times";
    }
  },
  template: `
  <button v-on:click="count++">
    Hi {{name}}, you clicked me {{ count }} {{times}}.
  </button>`
});
Vuejs props, data and computed properties

(Codesandbox Vuejs demo)

The case of React components

There are two interfaces: this.state and this.props. The first handles internal data of the component. The second handles external data of the component whether it comes from the calling component or the state manager.

class ButtonCounter extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  render() {
    const { count } = this.state;
    const { username } = this.props;
    const times = count === 1 ? "time" : "times";
    return (
      <button
        onClick={() => {
          this.setState({ count: count + 1 });
        }}
      >
        Hi {username}, you clicked me {count} {times}.
      </button>
    );
  }
}
React props and state

(Codesandbox Reactjs demo)

The case of Vuex

Whether the data is directly provided by the state of the store, or the data is derived, we don't have the same interface: this.state vs this.getters. And the consumer of the data has to be aware of the internal organization of the store, which is a bit disturbing.

const initialState = {
  todos: [
    { id: 1, text: 'Learn about State', done: true },
    { id: 2, text: 'Learn about Getters', done: false }
  ]
};

function doneTodos(state) {
  return state.todos.filter(todo => todo.done)
}

const store = new Vuex.Store({
  state: initialState,
  getters: {
    doneTodos: doneTodos
  }
})
Vuex state and getters

To access the todos, components can add a computed properties:

Vue.component("TodoList", {
  computed: {
    todos: function() {
      return this.$store.state.todos
    },
    doneTodos: function() {
      return this.$store.getters.doneTodos
    }
  },
  template: `
  <ul>
    <li v-for="todo in todos" :key="todo.text">
      {{ todo.text }}
    </li>
  </ul>`
});
Usage with Vuejs

The case of Redux

Getters are referred to as Selectors and handle providing derived data. In a redux vanilla framework, we will implement a function well-named mapStateToProps (transforming a data of the global state into a component's props property).

const initialState = {
  todos: [
    { id: 1, text: 'Learn about State', done: true },
    { id: 2, text: 'Learn about Selectors', done: false }
  ]
};

function doneTodos(state) {
  return state.todos.filter(todo => todo.done)
}

const store = createStore(function(state, action) {
  if (typeof state === 'undefined') {
    return initialState
  }
    
  return state;
});
Redux state and selectors

This creates container components (connected to the state manager).

class TodoList extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  render() {
    const { todos, doneTodos } = this.pros;
    return (
      <ul>
        {todos.map(todo => (
          <li>{todo.text}</li>
        ))}
      </ul>
    );
  }
}

const mapStateToProps = state => {
  return {
    todos: state.todos,
    doneTodos: doneTodos(state)
  }
};

const TodoListContainer = connect(mapStateToProps)(TodoList)
Usage with Reactjs

Conclusion

React and Redux don't do something special for computed properties or getters, they are only functions that take a raw data as a parameter and return a computed data.

Vuejs and Vuex treat computed properties and getters in a special way that force developers to adapt their code to the framework (in order to be reactive):

  1. The Vuejs component that consumes a Vuex data must know how the state is organised;
  2. Developers need to know the correct interface to access the state data regarding if they are raw or computed data.