Pure functional, simplified React-like Javascript library with built-in state management system for building reactive UIs.
This project is currently under active development.
This documentation assumes that you are not totally stranger to React, Redux and ES6. The following "Hello World" example is a glance of the usage of Veact:
// index.js
import Veact from 'veact'
import model from './model'
import App from './App'
Veact.createApp(
document.getElementById('root'),
model,
App
)
// App.js
import Veact from 'veact'
export default ({ app }) => <div>{ app.model.title }</div>
// model.js
export default {
title: 'Hello World'
}
The example uses JSX syntax because Veact implements the same "createElement" function as React does. Although JSX is not mandatory but it's like a syntax sugar that can boost the productivity. JSX is not natively supported by any Javascript runtime, to use it you need to enable a babel plugin called "@babel/plugin-transform-react-jsx" and to configure the .babelrc as follows:
{
"plugins": [
["@babel/plugin-transform-react-jsx", {
"pragma": "Veact.createElement"
}],
]
}
"model" is the place where all the data or state is stored, like store in Redux, there will be only one model in Veact, also known as the "single source of truth". Model has to be intialized and get passed in when the app is created. Then it will be accessible from any of your functional component. With the same principle as React or Redux has, "model" is not supposed to be modified directly, but by a "dispatch" function provided by the instance of your app which will trigger the update procress of both the virtual DOM and DOM:
// App.js
import Veact from 'veact'
const changeTitle = app = () => {
app.dispatch(model => ({
title: 'New Title'
}))
}
export default ({ app }) => <div onClick={changeTitle(app)}>{ app.model.title }</div>
Tips: When creating component-based functions, the advantage of using higher-order function which returns the actual function, is that the base-function will be only created once when the module is loaded.
Dispatch is the function to update model, and then update the virtual DOM and re-render the real DOM in the background. Unlike the "dispatch" in Redux, in Veact, things get simpler, dispatch only accepts one callback function with the current model as the default argument, it expects the callback to return a new model which will be merged to the current model. No direct modification to the model or any kinds of side-effects should be made:
// model.js
export default {
todos: ['todo 1', 'todo 2', 'todo 3'],
}
// App.js
import Veact from 'veact'
const addTodo = app = () => {
app.dispatch(model => ({
todos: [...model.todos].push('new todo')
}))
}
// A todo list
export default ({ app }) => (
<ul>
{
app.model.todos.map(todo => <li>{todo}</li>)
}
</ul>
<button onClick={addTodo(app)}>Add todo</button>
)
Wrong thing to do in the dispatch callback function:
const addTodo = app = () => {
app.dispatch(model => {
// Direct modification to the model is a side-effect!
model.todos.push('new todo')
return {
todos: model.todos
}
})
}
When dealing with more complex models, remember to always return the root property from which you are doing the modification to restore the intial structure of your model:
// model.js
export default {
user: {
status: {
isLoggedIn: false
}
}
}
// App.js
import Veact from 'veact'
const login = app = () => {
app.dispatch(model => ({
user: {
status: {
isLoggedIn: true
}
}
}))
}
export default ({ app }) => <button onClick={login(app)}>Login</button>
To create a component is to create a function which returns a virtual DOM object. To pass data from parent component to children component, simply declare the
// App.js
import Veact from 'veact'
import Header from './Header'
export default () => (
<div>
<Header title="Hello World"/>
</div>
)
// Header.js
export default ({ title }) => (
<header>
<h1>{ title }</h1>
</header>
)