A lightweight state management library for native DOM
npm run build
const App = Skuol.createComponent({
oncreate(){
this.el.innerHTML = `<h2>Hello Skuol!</h2>`
}
})
document.querySelector('body').appendChild(new App().el)
const store = new Skuol.Store({
state: {
counter: 0,
todos: []
},
commits: {
addTodo(state, name){
state.todos = [
...state.todos,
{id: state.counter++, name}
]
},
removeTodo(state, { todo }) {
state.todos = filter(t => t !== todo)
}
}
})
const Card = Skuol.createComponent({
tagName: 'li',
onrender(){
this.el.innerHTML = `${this.model.name}`
}
})
const CardList = Skuol.createCollection({
tagName: 'ul',
component: Card
})
Skuol.install(store)
const TodoList = Skuol.connect()(new CardList())
const App = Skuol.createComponent({
oncreate(){
this.el.innerHTML = `
<h2>Hello Skuol!</h2>
<div id='todos'></div>
`
this.el.querySelector('#todos').appendChild(new TodoList().el)
}
})
document.querySelector('body').appendChild(app.el)
const App = Skuol.createComponent({
oncreate({ dispatch }){
this.el.innerHTML = `
<h2>Hello Skuol!</h2>
<form>
<label for='name'>Name:</label>
<input id='name' name='name' required>
<button type='submit'>Create</button>
</form>
<div id='todos'></div>
`
this.el.querySelector('#todos').appendChild(new TodoList().el)
this.el.querySelector('form').addEventListener('submit', e => {
e.preventDefault()
const name = this.el.querySelector('#name').value
dispatch('addTodo', name)
})
}
})
Comparison with Skuol & vanilla js implementation in Stefan Krause's js-framework-benchmark
Creates a Component
class
shouldUpdate
(function(model: Object): boolean): Called whenevercomponent.update
is called. Decides whether to callcomponent.onrender
. The default comparator is !==.oncreate
(function(props: Object)): A lifecycle callback. Called whenComponent
instance is created.onrender
(function(props: Object)): A lifecycle callback. Called whenComponent
instance is created or updated.
Creates a Component
class which has a default onrender
implementation for an array of data. Array type is only allowed for the parameter of component.constructor
and component.update
id
(String): An unique key which represents each element of data.component
(Component): A childComponent
class which will receive an element of data.onrender
(Function): Called after the defaultonrender
is executed.
data
(Object): An initial data. WhenStore
is connected, this is ignored.props
(Object): Parameters which is passed to the lifecycle callbacks.
Represents this component's top level Element Node.
Represents this component's model. A constructor
or update
's argument is converted to this.
Calls onrender
. The call is determined by the result of shouldUpdate
Unmounts component.el
from its parent Node.
state
(Object): An initial immutable stateactions
({ [name: string]: Function }): A action handler receives an context object which representsstate
,dispatch
andcommit
. You should changestate
in commit handlers only.commits
({ [name: string]: Function }): A commit handler receives a shallow copy ofstate
object that will be an next state. Becausestate
should not be mutated, to change its properties, you should use Immutable update patterns
new Skuol.Store({
state: {
todos: []
},
actions: {
async addTodo({commit}, name){
const todo = await post('/todos')
commit('addTodo', todo)
}
},
commits: {
addTodo(state, todo){
state.todos = [ ...state.todos, todo ]
}
}
})
Returns a state
. You should change state
in commit handlers only.
Calls a actions[actionName] handler. If it's not exists, calls a commits[actionName] handler.
Creates a connector which connects Component
instances and a Store
instance. The store should be installed before creating a Component
instance.
select
(function(state): Object): A function which selects a subset ofStore
.state. Whenever the state is changed, the selected data is passed to thecomponent.update
toProps
(function(props: Object): Object): A function which returns lifecycle callback parameters.
Skuol.install(store)
const TodoList = Skuol.connect({
select: state => (
state.todos.filter(c => c.status === 'TODO')
),
toProps: ({dispatch}) => ({
moveCard: cardId => dispatch('moveCard', cardId, 'TODO')
})
})(CardList)
A function which creates a filter function. The function returns a cached result for the same arguments.
const activeTodos = Skuol.createFilter((todos, assignee) => {
const activeNames = assignee
.filter(a => a.active)
.map(a => a.name)
const actives = new Set(activeNames)
const hasAssignee = (card) => (
!actives.size ||
card.assignee.findIndex(n => actives.has(n)) >= 0
)
return todos.filter(hasAssignee)
})
const TodoList Skuol.connect({
select: state => (
activeTodos(state.todos, state.assignee)
.filter(c => c.status === 'TODO')
),
})(CardList)
Installs a Plugin
.
Plugin
({install: Function}): An install handler receives aComponent
. You can use aComponent.prototype
to inject component-wide properties orComponent.prototype.props
to add lifecycle callback parameters.
const MyStore = function(){
this.install = (Component) => {
// set instance properties
Component.prototype.$myStoreKey = this
// add lifecycle properties
Component.prototype.props.myDispatch = function(){}
}
}
Skuol.install(new MyPlugin())