VUE FREEZE!
So, What is it?
Vue Freeze just a simple state management whitout bloating API and Concept. Just states and actions. And also It will keep your state immutable.
I have simple Vue State management, Why do I need it?
Well, you right. simple Vue State Management just working. Keep the state in the store, and mutate it via actions. But the problem is, are you sure you can keep that behavior ( mutate via actions )? How if you are falling into anti pattern? caused by forget the pattern, you mutate your state directly. It will make your apps become unmaintable. Is that state management warn you to not to do that?
What do you mean? I'm pretty sure that I can keep that behavior by myself!
Sorry, I really didn't mean it. Oke you will be able to keep it. But, how about your collabs? You will oversee all your collabs by yourself? Are you sure if you're in the right team? Are you sure they will not forget the pattern?
Well, I can tell them later. So what is your advice?
To keep your state management in the right working pattern, you need some system that preventing you to do some direct mutate which is will be your problem in the future. So, I'll advise you to use immutable state which you can't mutate it directly. It mean you should mutate your state via actions. When you do wrong, it will not mutate the global state. Simple.
Immutable? Do you mean immutable.js?
Yeah, it's either best library to make an immutable data. But we'll not use it. why? because you need to learn some API and some concept again. How about using Freezer.js? besides the size is smaller, it also has a smaller API which you can learn in 5 or 10 minutes. To keep the Vue being simple, Vue Freeze will use Freezer.js! that's way it called Vue Freeze
I got it. So, what Vue Freeze do with my Vue?
Vue Freeze will inject your store to your VM instances. it means you can use your state and actions from your VM via state
and $action
method. Every time the global state change, it will change your VM state
too. But, don't worry, it will serve the old state too if you need it for comparing or doing something.
Stop talking and give me a code!
Hhhmm... Oke, let's give it a try!
Installation
Just include the vue-freeze.min.js to your porject. After that, you need to prepare two things. They are:
The State
// Let's make a state!
// Using plain javascript for easy understanding...
/*********
*
* First of all, we need to define each individual state.
* to keep our apps organized~
*
**********/
// UI states
var ui = {
msg: "Hello Vue!",
};
// User States
var user = { // for user states
current: {
role: "user"
},
}
/*********
*
* After define all states, we should
* put them to the global states~
* Single state for single apps
*
**********/
// Define the global states
var state = { ui, user };
All above is how we create a states. Now, we need some actions to mutate it.
The Action
/*********
*
* The next step is, make some actions for mutating our states
* Actions just a function contain some Freezer event
*
**********/
/**
* Make a global action, don't forget to give it an argument
* To pass the store, e.g. 'store'
*/
var action = function(store){
/**
* the writing style of the event name is up to you,
* because it's string based
*/
store.on('ui:update_msg', function(arg){
// mutate the ui.msg state
store.get().ui.set({ msg: arg });
});
/**
* The store callback pass some value
* @param {Mixed} arg -> passing new data
* @param {Object} old -> still bring the old state
* The old state will be readable, you can use it if neccessary
*/
store.on('user:change_role', function(arg,old){
// mutate user.current.role state
store.get().user.current.set({ role: arg });
});
};
Now we've just make a new store. but we haven't yet install it to our Vue App.
/*********
*
* The Final step, install the Vue Freeze plugin into your Vue app
*
**********/
// Install it!
Vue.use( VueFreeze, { state: state, action: action } );
We have state
and $action
Methods now!. So, let's have fun with the Vue app!
// Make a new Vue
var App = new Vue({
el: "#app",
methods: {
changeMsg: function(){
this.$action('ui:update_msg',"Hai, I'm Vue Freeze!");
},
changeRole: function(){
this.$action('user:change_role','admin');
}
}
});
The template code is....
<div id="app">
<h1>{{ state.ui.msg }}</h1>
<p>
<b>user role:</b> {{ state.user.current.role }}
</p>
<button @click="changeMsg">Change The Message!</button>
<button @click="changeRole">Change The Role!</button>
</div>
How do you think? Is it good for you? You can try the working code above here Or fork my Codepen here. If you want to make some todo app, you can fork the Todo MVC example here
I won't too quick to judge. But, How about the API?
Vue Freeze will provide you state
, $action
, and $store
. But you are recommended to use the state
and $action
only.
The API:
state
The state
will provide you the states which you've declared before.
// declare New State
var state = {
msg: "Hey You!"
};
// Once you install the Vue Freeze you will get this in your VM
this.state.msg; // 'Hey You'
// Vue freeze also make your state become immutable
this.state.msg = "I Change you!" // error!
$action
The $action
will connect you to your actions. It's just a trigger of your store event.
// Declare Action Container
// @param {Freezer} store
var action = function(store) {
store.on('msg:update', function(arg,old){
if(old.msg === arg) return false // Provide the old states
store.get().set({ msg: arg }); // Mutate the state
});
};
// Once you install the Vue Freeze you can trigger that event via your VM
this.$action('msg:update', 'Has Changed!');
console.log(this.state.msg) // 'Has Changed!'
$store
The $store
is just the store which Vue Freeze already built. It contain the Freezer Object with full features. You not recommended to use it, yet you know what you gonna do.
// Once you install the Vue Freeze
this.$store.on('alert', 'From VM is not safe!');
this.$store.trigger('alert'); // From VM is not safe!
Why is it not safe? because
// It access the core of Vue Freeze
var me = this;
// it can broke the update behaviour
this.$store.on('update', function (val) {
me.halo = val;
});
// it can mutate your state directly
this.$store.get().set({ msg: "Direct Mutation!" });
// It can make some actions which will hard to find in the large App and surely make your App become unmaintable
this.$store.on('msg:delete', function(val){
me.$store.get().remove('msg');
});
All those things probably can make you fall in the anti pattern. Instead access the store directly via your VM. You can access the store via actions container which is also have a full feature of Freezer Object.
Options
Vue Freeze have some installation options and their default value.
Vue.use(VueFreeze,{
state: {}, // the state
action: function(store){}, // actions container
live: true, // Behaviour of Freezer live update
});
live
Update is explained here.
FAQS
How The Immutable State Works?
Pretty simple you can take a look my immutability-test.vue
in my Vue Freeze Todo MVC Example. The code look like:
<template>
<div>
<p>Global State UI Typing : {{ state.ui.typing }}</p>
<button @click="tryMutate">Try Direct Mutate!</button>
</div>
</template>
<script>
export default {
methods: {
tryMutate(){
// here the immutable work~
// direct mutate will not affecting the global state
// it's just affecting the internal state only
this.state.ui.typing = !this.state.ui.typing
console.log('Immutabillity Test',this.state.ui.typing); // true
// yet the global action will affect to our global state
// this.$action('user:typing',true); // uncoment this
},
},
};
</script>
The Direct mutate will not affecting the global state. it's just affecting the internal state only. To change the global state, you need to mutate it via actions. Simple, right?
As you see here Global state.ui.typing
will triggers the todo app to display "User is typing....". But when we pushed the "Direct Mutate" Button, the text is not showing up. It caused by the immutability of the state. The button event just changes the internal state NOT the global state. The ONLY way to mutate our global state is via action.
How can I organize my actions into separated files?
Just repeat it! I will give you an example. let's say that we have 2 separated actions files in actions
folder.
// ./actions/todo.js
let todoActions = function(store){
store.on('todo:add', function(todo){
store.get().todoApp.todos.push(todo);
});
store.on('todo:delete', function(index){
store.get().todoApp.todos.splice(index,1);
});
};
export default todoActions;
todo.js
only handles the todos state only. While the actions for handles the ui state is put in the ui.js
// ./actions/ui.js
let UIActions = function(store){
store.on('ui:user_is_typing', function(typing){
store.get().ui.set({ typing: typing });
});
};
export default UIActions;
We've just made all actions of our todo app in separated files. Now we need to combine it in the single root action.
// ./action.js
import todoActions from './actions/todo.js';
import UIActions from './actions/ui.js';
let action = function(store){
todoActions(store);
UIActions(store);
};
export default action;
Done!