1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
/*!

# Consecuit
An experimental functional web UI framework that uses the Rust type system for hooks and more.

---

This crate is a library for building web UI with functional components and hooks.

See the [README](https://github.com/wishawa/consecuit) for an overview.

Also take a look at [our TodoMVC](https://wishawa.github.io/consecuit/todomvc) and [its source code](https://github.com/wishawa/consecuit/tree/main/examples/todomvc).

# Using Consecuit

Below, we show you how to

* Write and use hooks.
* Write and use components.
* Write and use container components.

The [the counters example](https://github.com/wishawa/consecuit/tree/main/examples/counters/src/lib.rs) is also good for getting started.

## Hooks

A Consecuit hook is a function that can call other Consecuit hooks.
So you can build your own hook by composing the basic hooks like [`use_state`][hooks::use_state], [`use_ref`][hooks::use_ref], and [`use_effect`][hooks::use_effect].

A Consecuit hook must be a function that takes 2 arguments:
Consecuit's [`HookBuilder`][construction::hook::HookBuilder], and the args, which can be any type.

The return type should not be named out concretely, but rather written as `impl Trait` of [`HookReturn`][construction::types::HookReturn], with the actual value as the generic parameter.

The signature for a `use_i32_state` hook might look something like this

```
use consecuit::prelude::*;
fn use_i32_state(cc: HookBuilder, initial_value: i32) -> impl HookReturn<(i32, Updater<i32>)>
```

In the function body, you can write code just like a normal function.

To use other hooks, call `cc.hook(<hook function>, <hook args>)`.

This will consume `cc` and return a new one, along with the return value of the hook, in a tuple like this `(cc. <hook return value>)`.

You then can do whatever you want with the `<hook return value>`, and you can use the use the returned `cc` to call more hooks.

```
let (cc, some_hook_result) = cc.hook(some_hook_function, some_hook_args);
println!("{:?}", some_hook_result + 1);
let (cc, another_hook_result) = cc.hook(another_hook_function, another_hook_args);
```

When you are done and want to return from your hook, return `(cc, <your hook return value>)`.

```
(cc, some_value)
```

Our `use_i32_state` might look like this in full:

```
use consecuit::prelude::*;
fn use_i32_state(cc: HookBuilder, initial_value: i32) -> impl HookReturn<(i32, Updater<i32>)> {
    // Use the `use_state` hook.
    let (cc, (number, updater)) = cc.hook(use_state, initial_value);
    // Print the value.
    prinln!("The state is {}", number.to_string());
    // Return
    (cc, (number, updater))
}
```

Take a look at [the counters example](https://github.com/wishawa/consecuit/tree/main/examples/counters/src/lib.rs),
specifically the function `counter_hook_extracted`, to see how we extract logic into a new hook.

## Components

A Consecuit component is a function that can call Consecuit hooks, and render other Consecuit components.
You can use the HTML components provided by the consecuit_html crate as building blocks for your components' UI.

A Consecuit component must be a function that takes 2 arguments:
Consecuit's [`ComponentBuilder`][construction::component::ComponentBuilder], and the props, which can be any type that is [`PartialEq`] and [`Clone`], and `'static` (i.e. not a borrow).
(a component can only have 1 props).

The return type should not be named out concretely, but rather written as `impl Trait` of [`ComponentReturn`][construction::types::ComponentReturn].
Here is an example:

```
use consecuit::prelude::*;
fn my_component(cc: ComponentBuilder, props: MyComponentProps) -> impl ComponentReturn
```

In the function body, you can write normal code, and call hooks the same way as for hook functions documented above.

You can also use the [`cc_tree`][consecuit_macros::cc_tree] macro to render other components. Like this:

```
use consecuit_html::prelude::*; // <- consecuit_html provides html components like div and span
cc_tree!(
    <div>
        <div {html_props().class_name("some-class-name").onclick(onclick_callback)}>
            <span>"I'm a string literal"</span>
            <span {html_props().class_name("span-text")}>{format!("anything that is AsRef<str> works")}</span>
            {props.some_text}
            <my_component { MyComponentProps {text: "hello"} } />
        </div>
        <div {html_props().class_name("nothing-here")} />
    </div>
)
```

As you might have figured out from the code above already:

* Use the name of the component function is the name of the tag.
* Put the props in curly braces beside the tag name.
If there are none, the macro will attempt to use the [`Default::default`] value (and error if the props doesn't implement Default).
* Use string literal or any [`AsRef<str>`] type in braces to create text node.

Here is an example of a component function:

```
use consecuit::prelude::*;
use consecuit_html::prelude::*;
fn show_plus_calculation(cc: ComponentBuilder, (lhs, rhs): (i32, i32)) -> impl ComponentReturn {
    let result = lhs + rhs;

    // These 2 lines are just to show how to use hooks
    let (cc, _some_example_1) = cc.hook(use_example_hook, 1234);
    let (cc, _some_example_2) = cc.hook(use_another_hook, 5678);

    // All there `h5`, `span`, `b` are from the `consecuit_html` crate.
    cc_tree!(
        <my_website_header />
        <h5>"Calculation Result:"</h5>
        <span>{lhs.to_string()}</span>
        <span {html_props().class_name("plus-sign")}>" + "</span>
        <span>{rhs.to_string()}</span>
        <span {html_props().class_name("equal-sign")}>" = "</span>
        <span><b>{result.to_string()}</b></span>
        <my_website_footer />
    )
}
```

## Container Components

Container components are components with 'hole', allowing you to give it children.
Most components in the `consecuit_html` crate are containers.

Container components return `impl ContainerReturn` rather than `impl ComponentReturn`

For example, you can give div and span childrens:

```
cc_tree!(
    <div>
        "This is a child I put in the div"
        "This is another child I put in the div"
        <span>"This is a child I put in the span in the div"</span>
    </div>
)
```

To create your own container component, add the `HOLE` attribute to the component you wish to hole.
This will "forward" the hole of that component.

For example:

```
fn my_container(cc: ComponentBuilder, _: ()) -> impl ContainerReturn {
    cc_tree!(
        <div>
            "The hole of the div below is forwarded to become the hole of my_container!"
            <div HOLE />
        </div>
    )
}
```

A container component must have exactly one hole.
*/

pub mod construction;
pub mod executor;
pub mod hooks;
pub mod locking;
mod stores;

pub use consecuit_macros;

pub use construction::mount;

pub mod prelude {
    /*! Just import me.

    */
    pub use crate::construction::{
        component::ComponentBuilder,
        hook::HookBuilder,
        subtrees::*,
        types::{ComponentReturn, ContainerReturn, DynComponentReturn, HookReturn},
    };
    pub use crate::executor::{batch_updates, run_later};
    pub use crate::hooks::*;
    pub use consecuit_macros::cc_tree;
}