# Examples
This section will detail code-heavy examples of how to use this library in specific modern UI Frameworks. Generally, using this library in a framework involves using an extension designed specifically for that framework. Frameworks currently with plugin support include (this list will grow as more plugins are created):
And frameworks that will have future plugin support include (this list may change over time):
# Vue
Previous sections of the documentation have alluded to Vue components and how to register Stores in a Vue application, but let's go into more detail about the different types of levers available when using this library in a Vue project.
# Basics
First, let's just go over the basics of how to bind a Store to a Vue application instance, using our simple Counter example from previous sections of the documentation. Here is what our folder structure might look like (typical for a small Vue project):
project/
├── package.json
└── src
├── views/
│ └── ...
├── components/
│ └── ...
├── index.js
├── router.js
└── store.js
...
As you can see above, we've included a placeholder store.js
file that will contain all code for the application store. As a refresher, let's re-define our Store from previous examples:
// contents of src/store.js
import { Store } from 'auora';
const store = new Store({
state: {
count: 0,
},
mutations: {
increment(state) {
state.count += 1;
}
}
actions: {
incrementAsync(store) {
return new Promise((resolve, reject) => {
store.commit('increment');
resolve();
});
}
}
});
With our store defined, we can bind it to the main Vue application instance using the store
keyword (after importing the Auora
plugin and initializing it):
// contents of index.js
import Auora from 'auora/ext/vue';
import counterStore from '@/store';
Vue.use(Auora);
const app = new Vue({
el: '#app',
store: counterStore
});
After binding the store, we can use it in components like:
<template>
<div>
<p>{{ counter }}</p>
<button @click="increment">Increment Counter</button>
<button @click="incrementAsync">Increment Counter Asynchronously</button>
</div>
</template>
<script>
export default {
name: 'counter',
store: {
state: ['counter'],
mutations: ['increment'],
actions: ['incrementAsync']
},
}
</script>
Note that there is a new store
keyword option that components can hook into for exposing parts of the store. The structure of that data will always be an Object, and requires the following format:
store: {
state: ['p1', 'p2'], // list of state params to expose as computed properties
mutations: ['m1', 'm2'], // list of mutations to expose as methods
mutations: ['a1', 'a2'], // list of actions to expose as methods
}
For context, the counter definition above is equivalent to:
export default {
name: 'counter',
computed: {
counter: {
get () {
return this.$store.state.counter
},
set (value) {
this.$store.commit('counter', value)
}
}
},
methods() {
increment() {
this.$store.commit('increment');
},
incrementAsync() {
return this.$store.dispatch('incrementAsync');
}
}
}
TIP
Note the use of computed properties to proxy access to state parameters. Since state variables are automatically bootstrapped with a setter mutation, these computed properties can be automatically generated. If you create other specific mutations that should be used for setting a state value, you can use a syntax similar to above for declaring that mutation in the set
block of a computed property.
As you can see above, all components will also have access to a $store
variable with the full store object. For example, to dispatch an action explicitly inside a component, use:
this.$store.dispatch('incrementAsync');
The syntax above is nice for explicitly showing what operations are available to specific components, but if we simply want to expose all available state metadata to a component, we can just set store: true
:
<template>
<div>
<p>{{ counter }}</p>
<button @click="increment">Increment Counter</button>
<button @click="incrementAsync">Increment Counter Asynchronously</button>
</div>
</template>
<script>
export default {
name: 'counter',
store: true,
}
</script>
This will automatically reflect all store actions and methods into your component. However, it's recommended for clarity that specific store state parameters and operations are explicitly specified in components.
Another valid form of shorthand is:
...
<script>
export default {
name: 'counter',
store: ['counter', 'increment', 'incrementAsync'],
}
</script>
This will automatically search for store actions, mutations, or parameters matching the specified names and will bind them to the component accordingly. The order of the search is actions → mutations → state.
# Modules
You can also utilize the concept of Modules when binding multiple stores to a Vue application instance. To illustrate this feature, let's consider the following application layout:
project/
├── package.json
└── src
├── index.js
└── modules/
├── profile/
│ ├── views/
│ ├── components/
│ ├── index.js
│ ├── router.js
│ └── store.js
├── feed/
│ ├── views/
│ ├── components/
│ ├── index.js
│ ├── router.js
│ └── store.js
...
In this project, we've defined separate stores for both the profile
and feed
sections of the application.
To bind these separate stores to the same Vue application instance, you can use the following syntax:
// contents of index.js
import Auora from 'auora/ext/vue';
import profile from '@/modules/profile/store';
import feed from '@/modules/feed/store';
Vue.use(Auora);
const app = new Vue({
store: {
profile,
feed,
}
});
Then, inside of components in your application, you can access those modules in a nested way like so:
this.$store.profile.state.param1;
this.$store.profile.dispatch('profileAction');
this.$store.feed.state.param2
this.$store.feed.dispatch('feedAction');
Using the store
property on components, we can expose specific functionality using the following syntax:
<script>
export default {
name: 'my-component',
store: {
profile: {
state: ['param1'],
actions: ['profileAction'],
},
feed: {
mutations: ['feedMutation'],
}
}
}
</script>
# Name Collision
One of the oldest gotchas in programming is unknowingly using the same name to represent different variables. To avoid name collision when using the store
block in a Vue component, you can specify an object with name mapping for state variables (or any other construct). Here is an example:
<template>
<div>
<h3>Extra Todos</h3>
<p v-for="todo in todos">{{ todo.text }}</p>
<h3>My Todos</h3>
<p v-for="todo in myTodos">{{ todo.text }}</p>
</div>
</template>
<script>
export default {
name: 'todo-component',
store: {
state: {
// local-name: state-param
myTodos: 'todos'
},
}
data() {
return {
todos: [
{ id: 1, text: 'extra todo 1' },
{ id: 2, text: 'extra todo 2' }
]
}
}
}
</script>
# Angular
You can still use this library in a Angular project, but you'll currently need to figure out your own patterns for incorporating it. A native Angular plugin leveraging this library will be included in a future release.
Help Wanted
If you're a Angular Pro and would like to help out by working on a Angular plugin, please file a discussion
ticket in the GitHub Issue Tracker for this project.
# React
You can still use this library in a React project, but you'll currently need to figure out your own patterns for incorporating it. A native React plugin leveraging this library will be included in a future release.
Help Wanted
If you're a React Pro and would like to help out by working on a React plugin, please file a discussion
ticket in the GitHub Issue Tracker for this project.