Hi guys!
My name is Dani. For several years now, I have been experimenting with different approaches to front-end development. I tried many different ideas, the best of which I collected in my framework Whatsup. In this article, I want to briefly tell you about architectural decisions and discuss them with you in the comments.
Short features list
- π easy to use: simple api, just write code
- π own reactivity system with high performance
- π cool styling system based on css modules
- β glitch free, autotracking and updating of dependencies
- π₯ written in typescript, type support out of the box
- π small size: ~7kB gzipped (state + jsx + cssx)
JSX components on generators
It seems to me that one day the React team really wanted to get rid of class components in order to leave only functional ones. But there was a problem - the functional components are called every time when rendering and you need to somehow transfer the state from render to render. They came up with hooks... Now we all use hooks... When javascript has generators... Just take a look at how using native language constructs you can describe the life cycle of a component.
function* App() {
// componentWillMount
try {
while (true) {
// main life cycle
yield <div>Hello</div>
}
} catch (e) {
// componentDidCatch
} finally {
// componentDidUnmount
}
}
It may seem unusual at first glance, but believe me - it's very simple. All variables you declare in the componentWillMount
phase will be available from render to render, no magic - that's the nature of generators.
With try{}catch{}
you can easily handle errors. And it's native javascript capabilities, isn't that great?
However, you are not required to write try{}catch{}finally{}
in each component, only where it's really needed. For example, we only need to control the componentWillMount
and componentDidUnmount
phases:
function* App() {
// componentWillMount
try {
while (true) {
// main life cycle
yield <div>Hello</div>
}
} finally {
// componentDidUnmount
}
}
Or we only need the componentWillMount
phase:
function* App() {
// componentWillMount
while (true) {
// main life cycle
yield <div>Hello</div>
}
}
And if we donβt need to control any phases at all, then we just use a regular functional component:
function App() {
return <div>Hello</div>
}
Mobx-like state management
I have been using React + Mobx for many years. I love that Mobx allows you to write intuitive code that is easy to read and maintain. But I always lacked the ability to use generators to create computed atoms.
const timer = computed(function*(){
const count = observable(0)
const intervalId = setInterval(()=> count(count() + 1), 1000)
try {
while(true){
yield count()
}
} finally {
clearInterval(intervalId)
}
})
autorun(()=> console.log(timer())
//> 1
//> 2
//> 3
In this example, all the components necessary for the timer to work are encapsulated in the body of the generator. I find this to be a very slick solution. Mobx does not provide us with such opportunities.
A computed atom can also be created from a normal function
const count = observable(0)
const text = computed(()=> `Count is: ${count()}`)
autorun(()=> console.log(text())
//> Count is: 0
count(1)
//> Count is: 1
As you may have noticed, getting a value from a computed or an observable is done with a call without arguments (count()
text()
), and setting a value in an observable is a call with an argument (count(1)
).
In all other respects, the state management API is very similar to Mobx and includes the following components:
- observable - creates a trackable atom
- array, map, set - creates a trackable array, map, set
- computed - creates a derived atom
- action, runInAction - allows multiple updates in one operation
- autorun, reaction - trigger side effects when observed values change
- mutator - allows you to create new data based on previous
CSSX styling system
This is a hybrid of css-modules and jsx namespaces, that uses the sass language to describe the styles of the components.
Consider this example:
// styles.scss
.box {
width: 50px;
height: 50px;
}
We can use it like regular css modules
import styles from './styles.scss'
function Box(){
return <div className={styles.box} />
}
We can bind styles to a component using the cssx function and then apply the .box
class to an element using a namespaced property css:box
import styles from './styles.scss'
import { cssx } from 'whatsup/cssx'
const Div = cssx('div', styles)
function Box(){
return <Div css:box />
}
Or we can immediately import the component with binded styles from the css file (this way we can import any standard html tag)
import { Div } from './styles.scss'
function Box(){
return <Div css:box />
}
Among other things, you can import style files and access their styles, for example:
// styles.scss
@import 'grid.scss';
.box {
width: 50px;
height: 50px;
}
And now we can arrange our box according to the grid rules
import { Div } from './styles.scss'
function Box(){
return <Div css:box css:sm_col_2 css:md_col_3 />
}
And for all this, Whatsup provides intellisense
Our first component
Well, let's summarize our knowledge and write our first component. Let's make a box that changes color on click.
// styles.scss
.box {
width: 50px;
height: 50px;
}
.coral {
background-color: coral;
}
.green {
background-color: green;
}
import { observable } from 'whatsup'
import { render } from 'whatsup/jsx'
import { Div } from './styles.scss'
export function Box() {
const color = observable('coral')
const onClick = () => color(color() === 'coral' ? 'green' : 'coral')
while (true) {
yield (
<Div
css:box
css:coral={color() === 'coral'}
css:green={color() === 'green'}
onClick={onClick}
/>
)
}
}
render(<Box />)
And we can look at the result and sources
Want to try?
Just run this in your terminal
npx @whatsup/cli create project
Conclusion
The frontend world is rapidly evolving. New projects come to replace the old ones. Only the most courageous and ambitious survive.I like to search and find original ideas, I hope that my article was useful to you.
Thank you for reading, and let's connect!
Links
whatsup / whatsup
A frontend framework for chillout-mode development π₯€ JSX components on generators*, fast mobx-like state management and exclusive cssx style system
What is it?
Whatsup is a modern frontend framework with own reactivity system and JSX components based on pure functions and generators.
Features
- π easy to use: simple api, just write code
- π own reactivity system with high performance
- π cool styling system based on css modules
- π¦ built-in router with intuitive api
- β glitch free, autotracking and updating of dependencies
- π₯ written in typescript, type support out of the box
- π small size: ~7kB gzipped (state + jsx + cssx)
Example
import { observable } from 'whatsup'
import { render } from 'whatsup/jsx'
function* App() {
const counter = observable(0)
const increment = () => counter(counter() + 1)
while (true) {
yield (
<div>
<p>You click {counter()} times</p>
<button onClick=
β¦
Top comments (5)
Woow, congratulations my friend!
I saw the documentation, and I thought it was amazing!
Thank you! Glad you liked it. Do you have a desire to try it in one of your projects?
In my personal projects, yep. π
cool stuff bro
Any youtube tutorial for this?
My spoken English is too bad to voice the video. But I'll try to come up with something.