상세 컨텐츠

본문 제목

실행 예제 2

Dioxus/시작하기

by 러스트코리아 2025. 11. 25. 23:24

본문

반응형

 

☆ UI 빌등

 

1. svg.rs

//! Thanks to @japsu and their project https://github.com/japsu/jatsi for the example!
//!
//! This example shows how to create a simple dice rolling app using SVG and Dioxus.
//! The `svg` element and its children have a custom namespace, and are attached using different methods than regular
//! HTML elements. Any element can specify a custom namespace by using the `namespace` meta attribute.
//!
//! If you `go-to-definition` on the `svg` element, you'll see its custom namespace.
 
use dioxus::prelude::*;
use rand::{Rng, rng};
 
fn main() {
    dioxus::launch(|| {
        rsx! {
            div { user_select: "none", webkit_user_select: "none", margin_left: "10%", margin_right: "10%",
                h1 { "Click die to generate a new value" }
                div { cursor: "pointer", height: "100%", width: "100%", Dice {} }
            }
        }
    });
}
 
#[component]
fn Dice() -> Element {
    const Y: bool = true;
    const N: bool = false;
    const DOTS: [(i64, i64); 7] = [(-1, -1), (-1, -0), (-1, 1), (1, -1), (1, 0), (1, 1), (0, 0)];
    const DOTS_FOR_VALUE: [[bool; 7]; 6] = [
        [N, N, N, N, N, N, Y],
        [N, N, Y, Y, N, N, N],
        [N, N, Y, Y, N, N, Y],
        [Y, N, Y, Y, N, Y, N],
        [Y, N, Y, Y, N, Y, Y],
        [Y, Y, Y, Y, Y, Y, N],
    ];
 
    let mut value = use_signal(|| 5);
    let active_dots = use_memo(move || &DOTS_FOR_VALUE[(value() - 1) as usize]);
 
    rsx! {
        svg {
            view_box: "-1000 -1000 2000 2000",
            onclick: move |event| {
                event.prevent_default();
                value.set(rng().random_range(1..=6))
            },
            rect { x: -1000, y: -1000, width: 2000, height: 2000, rx: 200, fill: "#aaa" }
            for ((x, y), _) in DOTS.iter().zip(active_dots.read().iter()).filter(|(_, active)| **active) {
                circle {
                    cx: *x * 600,
                    cy: *y * 600,
                    r: 200,
                    fill: "#333"
                }
            }
        }
    }

 

 

2. nested_listeners.rs 

//! Nested Listeners
//!
//! This example showcases how to control event bubbling from child to parents.
//!
//! Both web and desktop support bubbling and bubble cancellation.
 
use dioxus::prelude::*;
 
fn main() {
    dioxus::launch(app);
}
 
fn app() -> Element {
    rsx! {
        div {
            onclick: move |_| println!("clicked! top"),
            "- div"
            button {
                onclick: move |_| println!("clicked! bottom propagate"),
                "Propagate"
            }
            button {
                onclick: move |evt| {
                    println!("clicked! bottom no bubbling");
                    evt.stop_propagation();
                },
                "Dont propagate"
            }
            button {
                "Does not handle clicks - only propagate"
            }
        }
    }
}

 

 

☆ 상태 관리 

 

1. signals.rs

//! A simple example demonstrating how to use signals to modify state from several different places.
//!
//! This simple example implements a counter that can be incremented, decremented, and paused. It also demonstrates
//! that background tasks in use_futures can modify the value as well.
//!
//! Most signals implement Into<ReadSignal<T>>, making ReadSignal a good default type when building new
//! library components that don't need to modify their values.
 
use async_std::task::sleep;
use dioxus::prelude::*;
 
fn main() {
    dioxus::launch(app);
}
 
fn app() -> Element {
    let mut running = use_signal(|| true);
    let mut count = use_signal(|| 0);
    let mut saved_values = use_signal(|| vec![0.to_string()]);
 
    // use_memo will recompute the value of the signal whenever the captured signals change
    let doubled_count = use_memo(move || count() * 2);
 
    // use_effect will subscribe to any changes in the signal values it captures
    // effects will always run after first mount and then whenever the signal values change
    use_effect(move || println!("Count changed to {count}"));
 
    // We can do early returns and conditional rendering which will pause all futures that haven't been polled
    if count() > 30 {
        return rsx! {
            h1 { "Count is too high!" }
            button { onclick: move |_| count.set(0), "Press to reset" }
        };
    }
 
    // use_future will spawn an infinitely running future that can be started and stopped
    use_future(move || async move {
        loop {
            if running() {
                count += 1;
            }
            sleep(std::time::Duration::from_millis(400)).await;
        }
    });
 
    // use_resource will spawn a future that resolves to a value
    let _slow_count = use_resource(move || async move {
        sleep(std::time::Duration::from_millis(200)).await;
        count() * 2
    });
 
    rsx! {
        h1 { "High-Five counter: {count}" }
        button { onclick: move |_| count += 1, "Up high!" }
        button { onclick: move |_| count -= 1, "Down low!" }
        button { onclick: move |_| running.toggle(), "Toggle counter" }
        button { onclick: move |_| saved_values.push(count.to_string()), "Save this value" }
        button { onclick: move |_| saved_values.clear(), "Clear saved values" }
 
        // We can do boolean operations on the current signal value
        if count() > 5 {
            h2 { "High five!" }
        }
 
        // We can cleanly map signals with iterators
        for value in saved_values.iter() {
            h3 { "Saved value: {value}" }
        }
 
        // We can also use the signal value as a slice
        if let [first, .., last] = saved_values.read().as_slice() {
            li { "First and last: {first}, {last}" }
        } else {
            "No saved values"
        }
 
        // You can pass a value directly to any prop that accepts a signal
        Child { count: doubled_count() }
        Child { count: doubled_count }
    }
}
 
#[component]
fn Child(mut count: ReadSignal<i32>) -> Element {
    println!("rendering child with count {count}");
 
    rsx! {
        h1 { "{count}" }
    }
}

 

 

2. context_api.rs

//! Demonstrates cross-component state sharing using Dioxus' Context API
//!
//! Features:
//! - Context provider initialization
//! - Nested component consumption
//! - Reactive state updates
//! - Error handling for missing context
//! - Platform-agnostic implementation
 
use dioxus::prelude::*;
 
const STYLE: Asset = asset!("/examples/assets/context_api.css");
 
fn main() {
    launch(app);
}
 
#[component]
fn app() -> Element {
    // Provide theme context at root level
    use_context_provider(|| Signal::new(Theme::Light));
 
    rsx! {
        Stylesheet { href: STYLE }
        main {
            class: "main-container",
 
            h1 { "Theme Switcher" }
            ThemeControls {}
            ThemeDisplay {}
        }
    }
}
 
#[derive(Clone, Copy, PartialEq, Debug)]
enum Theme {
    Light,
    Dark,
}
 
impl Theme {
    fn stylesheet(&self) -> &'static str {
        match self {
            Theme::Light => "light-theme",
            Theme::Dark => "dark-theme",
        }
    }
}
 
#[component]
fn ThemeControls() -> Element {
    let mut theme = use_theme_context();
    let current_theme = *theme.read();
    rsx! {
        div {
            class: "controls",
            button {
                class: "btn",
                onclick: move |_| theme.set(Theme::Light),
                disabled: current_theme== Theme::Light,
                "Switch to Light"
            }
            button {
                class: "btn",
                onclick: move |_| theme.set(Theme::Dark),
                disabled: current_theme == Theme::Dark,
                "Switch to Dark"
            }
        }
    }
}
 
#[component]
fn ThemeDisplay() -> Element {
    let theme = use_theme_context();
 
    rsx! {
        div {
            class: "display {theme.read().stylesheet()}",
            p { "Current theme: {theme:?}" }
            p { "Try switching themes using the buttons above!" }
        }
    }
}
 
fn use_theme_context() -> Signal<Theme> {
    try_use_context::<Signal<Theme>>()
        .expect("Theme context not found. Ensure <App> is the root component.")
}

 

☆ 비동기 작업

 

1. futures.rs

//! A simple example that shows how to use the use_future hook to run a background task.
//!
//! use_future won't return a value, analogous to use_effect.
//! If you want to return a value from a future, use use_resource instead.
 
use async_std::task::sleep;
use dioxus::prelude::*;
 
fn main() {
    dioxus::launch(app);
}
 
fn app() -> Element {
    let mut count = use_signal(|| 0);
 
    // use_future is a non-reactive hook that simply runs a future in the background.
    // You can use the UseFuture handle to pause, resume, restart, or cancel the future.
    use_future(move || async move {
        loop {
            sleep(std::time::Duration::from_millis(200)).await;
            count += 1;
        }
    });
 
    // use_effect is a reactive hook that runs a future when signals captured by its reactive context
    // are modified. This is similar to use_effect in React and is useful for running side effects
    // that depend on the state of your component.
    //
    // Generally, we recommend performing async work in event as a reaction to a user event.
    use_effect(move || {
        spawn(async move {
            sleep(std::time::Duration::from_secs(5)).await;
            count.set(100);
        });
    });
 
    // You can run futures directly from event handlers as well. Note that if the event handler is
    // fired multiple times, the future will be spawned multiple times.
    rsx! {
        h1 { "Current count: {count}" }
        button {
            onclick: move |_| async move {
                sleep(std::time::Duration::from_millis(200)).await;
                count.set(0);
            },
            "Slowly reset the count"
        }
    }
}

 

 

2. suspense

//! Suspense in Dioxus
//!
//! Suspense allows components to bubble up loading states to parent components, simplifying data fetching.
 
use dioxus::prelude::*;
 
fn main() {
    dioxus::launch(app)
}
 
fn app() -> Element {
    rsx! {
        div {
            h1 { "Dogs are very important" }
            p {
                "The dog or domestic dog (Canis familiaris[4][5] or Canis lupus familiaris[5])"
                "is a domesticated descendant of the wolf which is characterized by an upturning tail."
                "The dog derived from an ancient, extinct wolf,[6][7] and the modern grey wolf is the"
                "dog's nearest living relative.[8] The dog was the first species to be domesticated,[9][8]"
                "by hunter–gatherers over 15,000 years ago,[7] before the development of agriculture.[1]"
            }
            h3 { "Illustrious Dog Photo" }
            ErrorBoundary { handle_error: |_| rsx! { p { "Error loading doggos" } },
                SuspenseBoundary { fallback: move |_| rsx! { "Loading doggos..." },
                    Doggo {}
                }
            }
        }
    }
}
 
#[component]
fn Doggo() -> Element {
    // `use_loader` returns a Result<Loader<T>, Loading>. Loading can either be "Pending" or "Failed".
    // When we use the `?` operator, the pending/error state will be thrown to the nearest Suspense or Error boundary.
    //
    // During SSR, `use_loader` will serialize the contents of the fetch, and during hydration, the client will
    // use the pre-fetched data instead of re-fetching to render.
    let mut dog = use_loader(move || async move {
        #[derive(serde::Deserialize, serde::Serialize, PartialEq)]
        struct DogApi {
            message: String,
        }
 
        reqwest::get("https://dog.ceo/api/breeds/image/random/")
            .await?
            .json::<DogApi>()
            .await
    })?;
 
    rsx! {
        button { onclick: move |_| dog.restart(), "Click to fetch another doggo" }
        div {
            img {
                max_width: "500px",
                max_height: "500px",
                src: "{dog.read().message}"
            }
        }
    }
}

 

반응형

'Dioxus > 시작하기' 카테고리의 다른 글

실행 예제  (0) 2025.11.25
개요  (0) 2025.11.24
첫 번째 Dioxus 앱  (0) 2025.11.20
설치 및 설정  (0) 2025.11.15
빠른 시작  (0) 2025.11.14

관련글 더보기