Crate consecuit[][src]

Expand description

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 for an overview.

Also take a look at our TodoMVC and its source code.

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 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, use_ref, and use_effect.

A Consecuit hook must be a function that takes 2 arguments: Consecuit’s 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, 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, 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, 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. 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 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.

Re-exports

pub use consecuit_macros;
pub use construction::mount;

Modules

construction

The important internal mechanisms are in here.

executor

Render queue and batching.

hooks

Essential hooks you can build other hooks with.

locking

Locking mechanism. Don’t mind this.

prelude

Just import me.