Data Fetching and API Integration in euv
Project Code:https://github.com/euv-dev/euv
Modern web applications rarely exist in isolation. They need to communicate with servers, fetch data from APIs, and send user input back to backend services. In euv, data fetching and API integration are handled through the framework's async operations support, reactive signal system, and event-driven architecture.
This article provides a comprehensive guide to data fetching and API integration in euv, covering everything from basic HTTP requests to advanced patterns for handling loading states, errors, and caching.
The Foundation: Async Operations in euv
euv has built-in support for asynchronous operations, which is essential for API calls. The framework integrates Rust's async/await syntax with its reactive signal system, allowing you to write asynchronous code that feels natural and integrates seamlessly with the UI.
When you need to fetch data from an API, you typically create an async function that performs the HTTP request and returns the result. This result can then be stored in a signal for the UI to react to.
Basic Data Fetching Pattern
The most common pattern for data fetching in euv involves three signals: one for the data, one for the loading state, and one for any errors that might occur. Here's how this pattern works:
let data: Signal<String> = use_signal(|| "".to_string());
let loading: Signal<bool> = use_signal(|| false);
let error: Signal<String> = use_signal(|| "".to_string());
When the user triggers a data fetch — for example, by clicking a button — you update the loading state, perform the async operation, and then update the data or error signal based on the result:
button {
onclick: move |_event: Event| {
loading.set(true);
error.set("".to_string());
// Async data fetching would happen here
// The result would be stored in the data signal
}
"Fetch Data"
}
This pattern gives you full control over the loading and error states, which you can use to provide feedback to the user in the UI.
Using Event Handlers for API Calls
In euv, user interactions are handled through event handlers like onclick, oninput, and onchange. These handlers are the natural place to trigger API calls in response to user actions.
For example, you might want to fetch user data when a form is submitted:
let username: Signal<String> = use_signal(|| "".to_string());
let errors: Signal<String> = use_signal(|| "".to_string());
button {
onclick: move |_event: Event| {
let mut validation_errors: Vec<String> = Vec::new();
if username.get().trim().is_empty() {
validation_errors.push("Username is required".to_string());
}
if validation_errors.is_empty() {
errors.set("".to_string());
// Trigger API call here
submitted.set(format!("Submitted: {}", username.get()));
} else {
errors.set(validation_errors.join("; "));
}
}
"Submit"
}
In this example, the onclick handler first validates the form, and if validation passes, it would trigger an API call to submit the data. The submitted signal stores the result of a successful submission.
Reactive Data Display with Computed Signals
Once you've fetched data from an API, you need to display it in the UI. euv's reactive signal system makes this straightforward. You can use the html! macro to render the data, and the UI will automatically update whenever the underlying signal changes.
For displaying fetched data, you can use conditional rendering to handle different states:
html! {
div {
if loading.get() {
span { "Loading..." }
} else if !error.get().is_empty() {
span { class: "error", error }
} else {
span { data }
}
}
}
This pattern ensures that the user always sees the current state of the data fetch — whether it's loading, has errored, or has completed successfully.
The watch! Macro for Reactive Data Fetching
The watch! macro can be used to automatically fetch data whenever a particular piece of state changes. This is useful for scenarios like search-as-you-type, where you want to fetch results whenever the user types a new query:
watch!(search_query, |query: String| {
if !query.trim().is_empty() {
// Trigger API call with the new query
loading.set(true);
// Fetch data and update signals
}
});
This watcher will fire every time the search_query signal changes, allowing you to automatically fetch new data without requiring the user to click a button.
Form Submission with API Integration
A common pattern in web applications is to collect user input through a form and then send it to an API. In euv, this involves combining form handling with async operations:
let username: Signal<String> = use_signal(|| "".to_string());
let email: Signal<String> = use_signal(|| "".to_string());
let errors: Signal<String> = use_signal(|| "".to_string());
let submitted: Signal<String> = use_signal(|| "".to_string());
html! {
div {
input {
r#type: "text"
placeholder: "Enter username"
value: username
oninput: move |event: Event| {
if let Some(target) = event.target()
&& let Ok(input) = target.clone().dyn_into::<HtmlInputElement>() {
username.updater.set(input.value());
}
}
}
button {
onclick: move |_event: Event| {
let mut validation_errors: Vec<String> = Vec::new();
if username.get().trim().is_empty() {
validation_errors.push("Username is required".to_string());
}
if email.get().trim().is_empty() {
validation_errors.push("Email is required".to_string());
}
if validation_errors.is_empty() {
errors.set("".to_string());
submitted.set(format!("Submitted: {}", username.get()));
} else {
errors.set(validation_errors.join("; "));
}
}
"Submit"
}
}
}
In this pattern, the form collects user input through signals, validates it on submit, and then would typically trigger an async API call to send the data to the server.
Handling Loading States
Loading states are crucial for providing good user experience during API calls. In euv, you can use a boolean signal to track whether an API call is in progress:
let loading: Signal<bool> = use_signal(|| false);
html! {
div {
if loading.get() {
span { "Loading..." }
} else {
// Display the fetched data
span { data }
}
}
}
You can also disable interactive elements while loading to prevent duplicate requests:
button {
disabled: loading
onclick: move |_event: Event| {
loading.set(true);
// Perform API call
}
if loading.get() { "Loading..." } else { "Fetch Data" }
}
Error Handling
Robust error handling is essential for any application that communicates with APIs. In euv, you can use a dedicated error signal to store and display error messages:
let error: Signal<String> = use_signal(|| "".to_string());
// When an API call fails:
error.set("Failed to fetch data. Please try again.".to_string());
// When the error is resolved:
error.set("".to_string());
You can combine error handling with conditional rendering to show error messages in the UI:
html! {
div {
if !error.get().is_empty() {
span { class: "error-message", error }
}
}
}
Using the html! Macro for Data-Driven Rendering
The html! macro in euv supports conditional rendering and list rendering, which are essential for displaying data fetched from APIs. You can use if/else blocks to handle different states, and for loops to render lists of data:
html! {
div {
if data.get().is_empty() {
span { "No data available" }
} else {
// Render the data
span { data }
}
}
}
For more complex data structures, you can use the for loop syntax in the html! macro to iterate over collections and render each item.
Component-Level Data Fetching
When building components that need to fetch their own data, you can encapsulate the data fetching logic within the component itself. The #[component] macro makes this clean and reusable:
#[derive(Clone, Default)]
struct MyCardProps {
title: &'static str,
onclick: Option<Rc<dyn Fn(Event)>>,
disabled: Signal<bool>
}
#[component]
pub fn my_card(node: VirtualNode<MyCardProps>) -> VirtualNode {
let MyCardProps { title, .. } = node.try_get_props().unwrap_or_default();
let children: VirtualNode = node.try_get_child_node();
html! {
div {
h3 { title }
children
}
}
}
While this is a general component example, you can create specialized data-fetching components that accept API endpoints as props and manage their own loading and error states.
Summary
Data fetching and API integration in euv follow a consistent pattern built around the framework's reactive signal system:
- Use signals for state: Store data, loading state, and errors in signals.
-
Trigger fetches from event handlers: Use
onclickand other event handlers to initiate API calls. -
Use
watch!for reactive fetching: Automatically fetch data when state changes. - Handle loading and error states: Provide feedback to the user during API calls.
- Use conditional rendering: Display different UI based on the current state.
- Encapsulate in components: Create reusable components for data fetching.
By following these patterns, you can build euv applications that communicate effectively with APIs while providing a smooth and responsive user experience. The combination of Rust's async capabilities and euv's reactive signal system makes data fetching both powerful and elegant.
Project Code:https://github.com/euv-dev/euv
Top comments (0)