This is a design experiment to construct the simplest stateless, reactive Javascript framework possible. The index.js
file defines a simple Todo app using the proposed architecture.
I want this framework to only use pure vanilla Javascript, I'm taking a lot of inspiration from Redux and the Elm language/architecture. The structure of this project is fairly straight forward:
- Define an empty model.
const model = Object.freeze(
{
input: '',
todos: []
}
)
- Create an Enum containing the messages the app will invoke.
const msg = Object.freeze(
{
ADD_TODO: 0,
SET_INPUT: 1
}
)
- Define action creators.
const setInput = (payload) => ({
type: msg.SET_INPUT,
payload
})
const addTodo = (payload) => ({
type: msg.ADD_TODO,
payload
})
- Update the model when an action is dispatched.
const update = (state, action) => {
switch (action.type) {
case msg.SET_INPUT:
return { ...state, input: action.payload }
case msg.ADD_TODO:
return { ...state, todos: [...state.todos, action.payload] }
default:
return state
}
}
- Render the DOM.
The syntax for HTML rendering is simple:
elementName(Array Object attributes, Array Object events, Array Object children)
For example, a button element with the class 'my-button', the text "Submit", and an onClick event will be this:
button([{ class: 'my-button' text: 'Submit' }], [{ onClick: dispatch(handlClick())}], [])
The following renders our Todo app:
const Todo = ({ text }) => (
li([{ text: text }], [], [])
)
const TodoList = () => {
ul([{ class: 'todo-list' }], [], state.todos.map((todo) => (
Todo(todo)
)))
}
const NewTodo = () => (
input([{ type: 'text' }], [{ onChange: dispatch(setInput(this.value)) }], []), // 'this' refers to the input DOM node
button([{ text: 'Add Todo' }], [{ onClick: dispatch(addTodo({ id: Date.now(), text: state.input })) }], [])
)
const render = () => (
TodoList(),
NewTodo()
)