vue-lens-mixin v1.0.0
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>
`,
});
License
6 years ago