Skip to content

Commit

Permalink
Expand API docs (#48)
Browse files Browse the repository at this point in the history
  • Loading branch information
Bloomca authored Jun 6, 2024
1 parent 3cf29d1 commit 91a45b0
Show file tree
Hide file tree
Showing 8 changed files with 144 additions and 53 deletions.
48 changes: 48 additions & 0 deletions docs/api/combine-state.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
layout: default
title: combineState
nav_order: 6
parent: API
---

## combineState

> Note: this function needs to be imported from `"veles/utils"`
> This function is also available as `combine`
Sooner or later you'll run into a situation where you depend on several states. While in theory you can make a subscription inside another one and access both current values that way, you should not do that. Not only it is cumbersome to write, it is also not very flexible and can't be used with other states.

To help with that, there is a `combineState` or `combine` function, which merges several states together for you. It accepts any number of states and creates a new state which will have an array with values from all passed states.

```jsx
import { createState } from "veles";
import { combineState } from "veles/utils";

function Component() {
const nameState = createState("");
const lastNameState = createState("");
const fullNameState = combineState(nameState, lastNameState);

return (
<div>
<input
type="text"
name="name"
onInput={(e) => nameState.setValue(e.target.value)}
value={nameState.useAttribute()}
/>
<input
type="text"
name="lastName"
onInput={(e) => lastNameState.setValue(e.target.value)}
value={lastNameState.useAttribute()}
/>
{fullNameState.useValueSelector(
([firstName, lastName]) => `Full name is ${firstName} ${lastName}`
)}
</div>
);
}
```

The advantage here is that we can combine several states, we can pass that state down as a prop to another components, we can use `trackValue` and do side-effects when either of them change.
2 changes: 0 additions & 2 deletions docs/api/create-element.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,3 @@ function Component() {
);
}
```

> `<Fragment>` cannot be returned directly from `useValue/useValueSelector` at the time
30 changes: 30 additions & 0 deletions docs/api/create-ref.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
layout: default
title: createElement
nav_order: 5
parent: API
---

## createRef

Since components don't re-render, and the component's code is executed only one time when the component is being mounted, you probably don't need refs for anything except for more convenient access to DOM elements. Every element accepts `ref` and it will be assigned the correct HTML node at the time of execution. If you want to pass the ref down, there is no need for wrapping components into anything, just pass it as any other prop (`ref` on components is ignored and treated like any other property).

```jsx
import { createRef } from "veles";
function App() {
const inputRef = createRef();

return (
<div>
<button
onClick={() => {
inputRef.current?.focus();
}}
>
focus input
</button>
<input type="text" ref={inputRef} />
</div>
);
}
```
47 changes: 47 additions & 0 deletions docs/api/select-state.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
layout: default
title: selectState
nav_order: 7
parent: API
---

## selectState

> Note: this function needs to be imported from `"veles/utils"`
> This function is also available as `select`
When working with multiple states, often by using [combineState](./combine-state.html), you end up with not the ideal state value representation. E.g. if you want to pass it down as a property to multiple component, you might want to avoid doing the same selector work on all of them, and want to do them at once. It becomes even more important if you want to combine that state with something else.

To change the state value, you can use `selectState`/`select` functions. Here is an example:

```jsx
import { createState } from "veles";
import { combineState, selectState } from "veles/utils";

function Component() {
const nameState = createState("");
const lastNameState = createState("");
const fullNameState = selectState(
combineState(nameState, lastNameState),
([firstName, lastName]) => `${firstName} ${lastName}`
);

return (
<div>
<input
type="text"
name="name"
onInput={(e) => nameState.setValue(e.target.value)}
value={nameState.useAttribute()}
/>
<input
type="text"
name="lastName"
onInput={(e) => lastNameState.setValue(e.target.value)}
value={lastNameState.useAttribute()}
/>
{fullNameState.useValue((fullName) => `Full name is ${fullName}`)}
</div>
);
}
```
19 changes: 19 additions & 0 deletions docs/frameworks-difference.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,25 @@ nav_order: 5

The main difference with other reactive based frameworks is that the tracking primitive (`createState` in Veles' case) allows for specific subscriptions, and that arrays are highly optimized and effectively will be rendered only one time, and all subsequent updates will only cause atomic changes in actual HTML.

Let's compare with a couple of popular libraries:

### React

Veles aims for a very similar composability as React provides, so the component approach should be pretty close, allowing for passing JSX down as props, logic reusability, etc.
Underneath there is a very big difference between these libraries. React re-renders components as soon as any of their internal states change, and then builds that into an internal presentation (Virtual DOM), and after comparing the existing and the new structure, applies updates to the changed components.

Veles approaches updates differently. Instead of global component state updates, all changes are atomic, and updates are applied only to relevant subscriptions. There is also no diffing algorithm, meaning that in case there is a new state, all subscribers will re-execute their code. Because of that, there is no concept of re-rendering, components mount only one time.

## Solidjs

[Solidjs](https://www.solidjs.com/) is a very close library to Veles conceptually. It also has atomic updates, the state primitive (Signals) is close to `createState`, and component lifecycle is pretty much identical: the body is executed one time, and then there is `mount` and `cleanup` events.

The implementation is slightly different. Solidjs tries to be more streamlined in its API, working as expected in most cases, but that comes with the expense that if you need a bit different approach, it might be a bit more cumbersome. By default Solidjs' Signal primitive does not allow for selector subscriptions; it does have a way to do so, but you need to use a different primitive for it.

You need to use specific components for control flows in Solidjs (conditionals, arrays), and also array elements are non-dynamic, which can cause potential issues if you have nested array within arrays.

Overall, with the correct approach, both libraries should give you good interactive performance.

## Veles' drawbacks

The library is pretty much in alpha state, and there are several missing concepts:
Expand Down
10 changes: 0 additions & 10 deletions docs/guides/index.md

This file was deleted.

34 changes: 0 additions & 34 deletions docs/guides/working-with-multiple-states.md

This file was deleted.

7 changes: 0 additions & 7 deletions docs/typescript.md

This file was deleted.

0 comments on commit 91a45b0

Please sign in to comment.