vue-lens-mixin
v1.0.0
Published
A mixin to use functional lenses as props, for state management
Downloads
2
Readme
Vue Lens Mixin
Functional and fractal state management for Vue.js using lenses. (Proof of concept and probably not optimized for performance! Probably also not optimized for the best API)
- Feels like local state, but is actually global state
- Synchronization of state through out the component tree is automatic
Installation
npm install --save vue-lens-mixin
Usage
Your top-most component should have a state
data containing the global state:
<script>
export default {
name: 'App',
data: function() {
return {
+ state: {temperature: 80, wind: 42},
};
},
};
</script>
For every child component that needs a conversion layer from/to this global state, define the lens object {get, set}
for the template.
<script>
export default {
name: 'App',
data: function() {
return {
state: {temperature: 80, wind: 42},
+ thermometerLens: {
+ get(state) {
+ const fahrenheit = state.temperature;
+ const celsius = Math.round(((fahrenheit - 32) * 5) / 9);
+ return celsius;
+ },
+
+ set(celsius, state) {
+ const fahrenheit = Math.round((celsius * 9) / 5 + 32);
+ return {...state, temperature: fahrenheit};
+ },
+ },
};
},
};
</script>
Notice that get
and set
is not the OOP style getters and setters, but instead its a pair of pure functions:
{
get: parentState => childState,
set: (newChild, oldParent?) => newParent
}
This lens should be passed on to child components using v-bind:lens=
, for instance:
<template>
<div id="app">
<span>{{JSON.stringify(state)}}</span>
<my-thermometer v-bind:lens="thermometerLens"/>
</div>
</template>
To implement a component that expects a lens as prop, use the lens mixin. Notice also this component now expects its data to be under state
:
import Vue from 'vue';
import * as lens from 'vue-lens-mixin';
Vue.component('my-thermometer', {
mixins: [lens],
template: `
<div>
<h1>European thermometer</h1>
<button v-on:click="state -= 2">Colder</button>
<button v-on:click="state += 2">Hotter</button>
<h1>{{state}}°C</h1>
</div>
`,
});
This can be done for grandchildren components too. For instance, if my-thermometer
has a child, just define a lens object in the my-thermometer
, and render the child by passing the lens to it as props:
import Vue from 'vue';
import * as lens from 'vue-lens-mixin';
Vue.component('my-thermometer', {
mixins: [lens],
+ data: function () {
+ return {
+ childLens: {get: /* ... */, set: /* ... */}
+ }
+ },
template: `
<div>
<h1>European thermometer</h1>
<button v-on:click="state -= 2">Colder</button>
<button v-on:click="state += 2">Hotter</button>
<h1>{{state}}°C</h1>
+ <child-component v-bind:lens="childLens"/>
</div>
`,
});