I created a framework with JSX components on generators*, a fast mobx-like state management and an exclusive cssx styling system
Tags: webdev, react, javascript, typescript
Published: 2022-07-27 19:40:08

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](https://github.com/whatsup/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.

```tsx
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:

```tsx
function* App() {
    // componentWillMount
    try { 
        while (true) { 
            // main life cycle
            yield <div>Hello</div>
        }
    } finally {
        // componentDidUnmount
    }
}
```

Or we only need the `componentWillMount` phase:

```tsx
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:

```tsx
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.

```tsx
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

```tsx
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: 

```scss
// styles.scss

.box {
    width: 50px;
    height: 50px;
}
```

We can use it like regular css modules

```tsx
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`

```tsx
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)

```tsx
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:

```scss
// styles.scss
@import 'grid.scss';

.box {
    width: 50px;
    height: 50px;
}
```

And now we can arrange our box according to the grid rules

```tsx
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

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2vjs4tdrswn9144p96ow.png)

## Our first component

Well, let's summarize our knowledge and write our first component. Let's make a box that changes color on click.

```scss
// styles.scss 
.box {
    width: 50px;
    height: 50px;
}
.coral {
    background-color: coral;
}
.green {
    background-color: green;
}
```

```tsx
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](http://examples.whatsup.js.org/color-box) and [sources](https://github.com/whatsup/examples/tree/master/src/color_box/app/color_box)

## Want to try?

Just run this in your terminal

```bash
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
- [Sources on github](https://github.com/whatsup/whatsup)
- [Documentation](https://whatsup.js.org)
- [More examples](http://examples.whatsup.js.org) and it [sources](https://github.com/whatsup/examples)

{% github whatsup/whatsup%}