React State Management Made Easy! A Complete Guide to the Basics of F-Box
1. Introduction Have you ever found your code becoming increasingly complex and hard to maintain when using TypeScript, due to state management, asynchronous processes, and exception handling? F-Box is a TypeScript library that provides a mechanism for handling state and processes in a type-safe manner. It can also be applied to state management in React. What you’ll learn in this article How to manage state using F-Box Concrete examples of using F-Box within React components A hands-on experience of F-Box’s convenience by building a simple Todo app Through simple examples, this article aims to help you start using F-Box without difficulty. Let’s dive right in! 2. What is F-Box? When you hear about state management in React, you might think, “Is it like Redux?” or “Is it similar to Jotai?” But F-Box is a bit different; it’s a tool that lets you handle state management, asynchronous processes, and exception handling under a consistent concept. Documentation f-box-docs.com 2.1 Features of F-Box F-Box utilizes five structures known as “Boxes” to efficiently manage data and state. This provides a unified mechanism for handling state management, asynchronous operations, and exception handling. It can be used in any TypeScript environment, including back-end systems, as a core library (f-box-core). In addition, a dedicated hook library for React (f-box-react) is available, enabling a seamless development experience from back-end to front-end. 2.2 Problems F-Box Solves Here’s a quick look at the kinds of situations where F-Box can come in handy, along with simple examples. 1. Simplifying State Management In React, sharing state across components can involve cumbersome prop passing. By using RBox from F-Box, you can wrap and manage state so it’s easily shared among multiple components. Furthermore, the useRBox hook integrates changes to RBox’s state with React’s lifecycle. Example: Sharing State Below is an example of sharing a counter state in React. import { RBox } from "f-box-core" import { useRBox, set } from "f-box-react" // Create an RBox to manage the state const countBox = RBox.pack(0) function Parent() { const [count] = useRBox(countBox) // Get the current value from RBox return ( Count: {count} ) } function Child() { const [count] = useRBox(countBox) // Refer to the same RBox const setCount = set(countBox) // Retrieve the function for updating the state return ( setCount(count + 1)}> Increment (Current: {count}) ) } In this example, the parent and child components share the same state through countBox. You don’t need a Context or Provider to share state. By using useRBox to reference the value in the RBox reactively, updating the RBox will cause the component to re-render. 2. Centralizing Asynchronous and Error Handling F-Box provides the following mechanisms for simplifying the management of asynchronous operations and error handling: Wrap asynchronous processes and clarify their execution timing. Use a unified approach to handle success and failure states, organizing errors and exceptions. Safely check whether a value “exists” or “does not exist.” Example: Asynchronous Processing Below is an example of fetching string data from an API and storing it in state. import { Task, RBox, set } from "f-box-core" // Set the initial state const dataBox = RBox.pack("No data available.") // Wrap an asynchronous operation const fetchData = Task.tryCatch( async () => { const response = await fetch("https://api.example.com/data") if (!response.ok) throw new Error("Failed to fetch.") return await response.text() // On success }, (error) => error.message // On failure ) // Execute the asynchronous operation and store the result in state fetchData[""](set(dataBox)).run() Example: Checking for Value Existence Below is an example of safely handling the presence or absence of a value using the Maybe type. import { Maybe } from "f-box-core" // Value exists const maybeValue = Maybe.just("Hello, F-Box!") // Value does not exist const noValue = Maybe.nothing() // Check the value const result1 = maybeValue.match( (value) => `Value exists: ${value}`, // If the value exists () => "No value available" // If the value does not exist ) const result2 = noValue.match( (value) => `Value exists: ${value}`, // If the value exists () => "No value available" // If the value does not exist ) console.log(result1) // Value exists: Hello, F-Box! console.log(result2) // No value available We’ll dive deeper into the specific types and how they solve real-world problems in the next chapter! 3. Key Concepts of F-Box 3.1 The 5 “Box” Types Provided by F-Box F-Box offers 5 different Box types for wrapping and managing values or states. These types make it possible to handle data and state safel
1. Introduction
Have you ever found your code becoming increasingly complex and hard to maintain when using TypeScript, due to state management, asynchronous processes, and exception handling?
F-Box is a TypeScript library that provides a mechanism for handling state and processes in a type-safe manner. It can also be applied to state management in React.
What you’ll learn in this article
- How to manage state using F-Box
- Concrete examples of using F-Box within React components
- A hands-on experience of F-Box’s convenience by building a simple Todo app
Through simple examples, this article aims to help you start using F-Box without difficulty. Let’s dive right in!
2. What is F-Box?
When you hear about state management in React, you might think, “Is it like Redux?” or “Is it similar to Jotai?” But F-Box is a bit different; it’s a tool that lets you handle state management, asynchronous processes, and exception handling under a consistent concept.
Documentation
2.1 Features of F-Box
F-Box utilizes five structures known as “Boxes” to efficiently manage data and state.
This provides a unified mechanism for handling state management, asynchronous operations, and exception handling.
It can be used in any TypeScript environment, including back-end systems, as a core library (f-box-core
).
In addition, a dedicated hook library for React (f-box-react
) is available, enabling a seamless development experience from back-end to front-end.
2.2 Problems F-Box Solves
Here’s a quick look at the kinds of situations where F-Box can come in handy, along with simple examples.
1. Simplifying State Management
In React, sharing state across components can involve cumbersome prop passing. By using RBox
from F-Box, you can wrap and manage state so it’s easily shared among multiple components. Furthermore, the useRBox
hook integrates changes to RBox’s state with React’s lifecycle.
Example: Sharing State
Below is an example of sharing a counter state in React.
import { RBox } from "f-box-core"
import { useRBox, set } from "f-box-react"
// Create an RBox to manage the state
const countBox = RBox.pack(0)
function Parent() {
const [count] = useRBox(countBox) // Get the current value from RBox
return (
<div>
<p>Count: {count}p>
<Child />
div>
)
}
function Child() {
const [count] = useRBox(countBox) // Refer to the same RBox
const setCount = set(countBox) // Retrieve the function for updating the state
return (
<button onClick={() => setCount(count + 1)}>
Increment (Current: {count})
button>
)
}
In this example, the parent and child components share the same state through countBox
. You don’t need a Context
or Provider
to share state. By using useRBox
to reference the value in the RBox reactively, updating the RBox will cause the component to re-render.
2. Centralizing Asynchronous and Error Handling
F-Box provides the following mechanisms for simplifying the management of asynchronous operations and error handling:
- Wrap asynchronous processes and clarify their execution timing.
- Use a unified approach to handle success and failure states, organizing errors and exceptions.
- Safely check whether a value “exists” or “does not exist.”
Example: Asynchronous Processing
Below is an example of fetching string data from an API and storing it in state.
import { Task, RBox, set } from "f-box-core"
// Set the initial state
const dataBox = RBox.pack("No data available.")
// Wrap an asynchronous operation
const fetchData = Task.tryCatch(
async () => {
const response = await fetch("https://api.example.com/data")
if (!response.ok) throw new Error("Failed to fetch.")
return await response.text() // On success
},
(error) => error.message // On failure
)
// Execute the asynchronous operation and store the result in state
fetchData["<$>"](set(dataBox)).run()
Example: Checking for Value Existence
Below is an example of safely handling the presence or absence of a value using the Maybe
type.
import { Maybe } from "f-box-core"
// Value exists
const maybeValue = Maybe.just("Hello, F-Box!")
// Value does not exist
const noValue = Maybe.nothing()
// Check the value
const result1 = maybeValue.match(
(value) => `Value exists: ${value}`, // If the value exists
() => "No value available" // If the value does not exist
)
const result2 = noValue.match(
(value) => `Value exists: ${value}`, // If the value exists
() => "No value available" // If the value does not exist
)
console.log(result1) // Value exists: Hello, F-Box!
console.log(result2) // No value available
We’ll dive deeper into the specific types and how they solve real-world problems in the next chapter!
3. Key Concepts of F-Box
3.1 The 5 “Box” Types Provided by F-Box
F-Box offers 5 different Box types for wrapping and managing values or states. These types make it possible to handle data and state safely and efficiently, reducing complexity around errors or asynchronous processes.
- Box: The simplest type, wrapping values and providing operations.
- RBox: Enables reactive state management.
- Task: Wraps and manages asynchronous operations.
- Maybe: Manages whether a value “exists” or “does not exist.”
- Either: Distinguishes between “success” and “failure” outcomes.
Below, we’ll look at the features and usage of these types in detail.
3.2 Roles and Usage of Each Box Type
1. Box: Basic Value Wrapping
Box
is the most basic type, wrapping a value and providing basic operations.
- Use Case: Simple value manipulation.
-
Key Methods:
-
map
: Applies a function to the wrapped value and returns a new Box.
-
Example: Wrapping and Manipulating a Value
import { Box } from "f-box-core"
const numberBox = Box.pack(5)
// Apply a function to the value
const doubledBox = numberBox.map((n) => n * 2)
console.log(doubledBox.getValue()) // 10
2. RBox: The Core of State Management
RBox
(Reactive Box) wraps state for reactive management. When the state changes, it automatically notifies its subscribers.
- Use Case: Shared state and reactive notifications.
-
Key Methods:
-
subscribe
: Registers a function that’s called when the state changes. -
setValue
: Updates the state.
-
Example: Managing and Notifying State Changes
import { RBox } from "f-box-core"
const countBox = RBox.pack(0)
// Subscribe to state changes
countBox.subscribe((newValue) => {
console.log("New value:", newValue)
})
// Update the state
countBox.setValue((prev) => prev + 1) // Prints "New value: 1" to the console
3. Task: Wrapping Asynchronous Operations
Task
is a type for wrapping asynchronous processes. Rather than dealing with Promise
directly, it wraps a function of the form
, which delays execution and allows you to evaluate the asynchronous process at the desired timing.
- Use Case: Wrapping asynchronous operations (e.g., API calls).
-
Key Methods:
-
tryCatch
: Safely wraps an asynchronous process. -
run
: Executes the asynchronous process.
-
Example: Executing an Asynchronous Operation
import { Task } from "f-box-core"
// Wrap the asynchronous process
const fetchData = Task.tryCatch(
async () => {
const response = await fetch("https://api.example.com/data")
if (!response.ok) throw new Error("Failed to fetch.")
return await response.text()
},
(error) => `Error: ${error.message}`
)
// Use run to execute the asynchronous process (Promise is evaluated here)
fetchData.run().then((result) => console.log("Result:", result))
4. Maybe: Managing Optional Values
Maybe
is a type that handles whether a value “exists” or “does not exist” in a safe manner.
- Use Case: Handling optional values (e.g., API responses, user input).
-
Key Methods:
-
match
: Separates logic for the case where a value exists and where it does not.
-
Example: Checking for Value Existence
import { Maybe, RBox } from "f-box-core"
import { useRBox } from "f-box-react"
// Set the initial state
const maybeBox = RBox.pack<Maybe<string>>(Maybe.just("Hello, F-Box!"))
function Example() {
const [maybeValue] = useRBox(maybeBox)
return maybeValue.match(
(value) => <p>Value exists: {value}p>, // If a value exists
() => <p>No value availablep> // If a value does not exist
)
}
5. Either: Distinguishing Between Success and Failure
Either
is a type that distinguishes an operation’s result as either “success” or “failure.” It takes two type parameters, L
(Left: the failure type) and R
(Right: the success type).
- Use Case: When you need to explicitly differentiate between success and failure outcomes.
-
Key Methods:
-
match
: Separates logic for success and failure cases.
-
Example: Distinguishing Outcomes
Below is an example that uses Either
to display different states for success or failure.
import { Either, RBox } from "f-box-core"
import { useRBox } from "f-box-react"
// Set the initial state
// L: The failure type (string)
// R: The success type (string)
const resultBox = RBox.pack<Either<string, string>>(
Either.right("Operation successful")
)
function Example() {
const [result] = useRBox(resultBox)
return result.match(
(error) => <p style={{ color: "red" }}>Error: {error}p>, // On failure
(success) => <p style={{ color: "green" }}>Success: {success}p> // On success
)
}
Note on Either’s Type Parameters
-
L
(Left): The failure type (e.g., an error message or exception info). -
R
(Right): The success type (e.g., operation result).
By using the match
method on Either
, you can cleanly separate the logic for success (Right) and failure (Left).
3.3 Basic Operations in F-Box
These 5 Box types in F-Box support operators (method aliases) for flexible value transformations and combinations. This makes it straightforward to create new derived states from existing Box values, allowing intuitive, concise code for state management.
These operators are typically aliases of conventional methods (e.g., map
) named <$>
, etc. By using these operators, you can make your intent clearer and write more readable code, especially when chaining multiple state operations.
Below are examples of the main F-Box operators: map
(<$>
), apply
(<*>
), and flatMap
(>>=
). These method are common to all Box types.
1. map (<$>
): Create a Derived Box
map
creates a new derived Box from the value in a Box. The original Box remains unchanged, and a new Box is returned.
import { RBox } from "f-box-core"
const countBox = RBox.pack(10)
// Create a derived Box that doubles the value
const doubleCountBox = countBox["<$>"]((count) => count * 2)
// Or using regular method calls
const doubleCountBoxAlt = countBox.map((count) => count * 2)
console.log(countBox.getValue()) // 10 (original Box)
console.log(doubleCountBox.getValue()) // 20 (derived Box)
console.log(doubleCountBoxAlt.getValue()) // 20 (derived Box)
2. apply (<*>
): Create a Derived Box from Multiple Box Types
apply
combines multiple Box types to create a new derived Box. First, you wrap a function in a Box, then use apply
to apply it to other Box values.
import { RBox } from "f-box-core"
const addBox = RBox.pack((a: number) => (b: number) => a + b) // Wrap an addition function in a Box
const aBox = RBox.pack(10)
const bBox = RBox.pack(20)
// Create a derived Box that adds two values (applicative style)
const sumBox = addBox["<*>"](aBox)["<*>"](bBox)
// Or using regular method calls
const sumBoxAlt = addBox.apply(aBox).apply(bBox)
console.log(sumBox.getValue()) // 30
console.log(sumBoxAlt.getValue()) // 30
3. flatMap (>>=
): Create a Dynamically Derived Box
flatMap
dynamically generates a new Box based on the value of the original Box and returns it. This is useful for handling dynamic dependencies or conditional branching, allowing you to write complex logic concisely.
import { RBox } from "f-box-core"
const isEvenBox = RBox.pack(true) // true: even, false: odd
const evenBox = RBox.pack("Even")
const oddBox = RBox.pack("Odd")
// Create a derived Box that dynamically switches based on the condition
const resultBox = isEvenBox[">>="]((isEven) => (isEven ? evenBox : oddBox))
// Or using regular method calls
const resultBoxAlt = isEvenBox.flatMap((isEven) => (isEven ? evenBox : oddBox))
// Check the value
console.log(resultBox.getValue()) // "Even" (initial state)
// It switches dynamically
isEvenBox.setValue(false)
console.log(resultBox.getValue()) // "Odd" (isEvenBox changed to false)
F-Box supports both the typical method-call style and operator style like <$>
, <*>
, and >>=
.
Operator style is particularly handy when you want to concisely and clearly express a chain of state management operations. Also, you can still get suggestions from editors like VSCode (as if you’re calling a regular method) when you write it as ["<$>"]
.
Note
These operators are based on algebraic structures known as Functor (<$>
), Applicative (<*>
), and Monad (>>=
). You don’t need to understand these concepts to use F-Box effectively, but if you’re curious about the background, the following article provides a clear explanation:
In the next chapter, we’ll discuss in more detail how to leverage these operations in React!
4. Using F-Box with React
4.1 What Is F-Box React?
F-Box React is an extension library that helps you use F-Box in React. With RBox integrated into React hooks, it enables intuitive and reactive state management in React components.
Key Features
-
Type-safe, intuitive state management with the
useRBox
hook. - Efficient creation of derived states.
- Design considerations for optimal re-rendering performance.
4.2 The Basic Structure of useRBox
useRBox
can be used in four main ways:
- Manage a primitive value as an initial state.
- Use an existing RBox.
- Efficiently create derived states.
- Create derived states by combining multiple states.
Let’s look at each approach in turn.
4.3 Basic Patterns for useRBox
1. Managing a Primitive Value
If you pass a primitive value directly to useRBox
, it creates a new RBox. It returns both the current value and the RBox instance.
function Counter() {
const [count, countBox] = useRBox(0) // Create an RBox with initial value 0
return (
<div>
<p>Count: {count}p>
<button onClick={() => countBox.setValue((prev) => prev + 1)}>+1button>
div>
)
}
2. Using an Existing RBox
If there is an RBox already defined, you can simply pass it to useRBox
. In this case, the same RBox is used as-is.
const countBox = RBox.pack(0)
function Counter() {
const [count] = useRBox(countBox)
return (
<div>
<p>Count: {count}p>
<button onClick={() => countBox.setValue((prev) => prev + 1)}>+1button>
div>
)
}
3. Efficiently Creating a Derived State
By passing a function to useRBox
, you can delay the evaluation of creating a derived state. This prevents unnecessary recalculations while letting you define derived states concisely.
function Counter() {
const [count, countBox] = useRBox(0) // Generate with initial value 0
const [isEven, isEvenBox] = useRBox(() => countBox["<$>"]((v) => v % 2 === 0))
return (
<div>
<p>Count: {count}p>
<p>Is Even: {isEven ? "Yes" : "No"}p>
<button onClick={() => countBox.setValue((prev) => prev + 1)}>+1button>
div>
)
}
4. Creating a Derived State from Multiple States
When you combine multiple RBoxes to create a derived state, you can use the applicative style with apply
.
const itemCountBox = RBox.pack(2) // Number of items
const itemPriceBox = RBox.pack(100) // Price per item
const calculateTotal = (count: number) => (price: number) => count * price
function ShoppingCart() {
const [totalPrice] = useRBox(() =>
RBox.pack(calculateTotal)["<*>"](itemCountBox)["<*>"](itemPriceBox)
)
return (
<div>
<p>Total Amount: {totalPrice} yenp>
div>
)
}
By using useRBox
in F-Box, state management in React applications becomes more efficient in the following ways:
- Simple state management: Easily handle both primitive values and existing RBoxes.
- Derived state creation: Use lazy evaluation and memoization for efficient derived states.
In the next chapter, we’ll apply these concepts to create a practical Todo app!
5. Tutorial: Building a Todo App with F-Box
The best way to fully understand F-Box is to build an app with it. In this chapter, we’ll use React and F-Box to build a simple Todo app with the following features:
- Adding tasks: Add new tasks to a list.
- Completing tasks: Mark tasks as complete.
- Deleting tasks: Remove tasks from the list.
- Filtering tasks: Filter tasks by complete, incomplete, or all.
5.1 Setting Up Your Environment
Project Setup
- Create a React project using Vite Vite is a tool that lets you quickly set up a frontend application. Use the following commands:
npm create vite@latest f-box-todo-app --template react-ts
cd f-box-todo-app
-
Install F-Box for your version of React
Depending on which version of React you are using, install the corresponding version of
f-box-react
:
-
For React v19:
npm install react@latest react-dom@latest npm install -D @types/react@latest @types/react-dom@latest npm install f-box-core f-box-react@latest
or
npm install f-box-react@0.2
-
For React v18:
npm install react@18 react-dom@18 npm install -D @types/react@18 @types/react-dom@18 npm install f-box-core f-box-react@0.1
- ESLint Rule Configuration Adding the following rule will prevent errors when writing multi-line code:
{
"rules": {
"no-unexpected-multiline": "off"
}
}
- Starting the Development Server Once you’re done setting up, start the development server with:
npm run dev
5.2 Defining Global State
Create a new file called state.ts
and add the following:
// state.ts
// Definition of global state and derived state
import { RBox } from "f-box-core"
// Define the type for a Todo
export type Todo = {
id: number
text: string
completed: boolean
date: Date
}
// Define the type for the filter
export type FilterType = "all" | "completed" | "incomplete"
// RBox to manage the entire list of todos
export const todosBox = RBox.pack<Todo[]>([])
// RBox to manage the current filter type
export const filterBox = RBox.pack<FilterType>("all")
// Function to filter todos based on the filter type
const todosFilter = (type: FilterType) => (todos: Todo[]) =>
todos.filter((todo) => todo.completed === (type === "completed"))
// Derived RBox to determine whether filtering is needed based on the filter type
const shouldFilterBox = filterBox["<$>"]((type) => type !== "all")
// Derived RBox that retrieves the filtered list of todos
export const filteredTodosBox = shouldFilterBox[">>="]((shouldFilter) =>
shouldFilter
? // Apply filtering in an applicative style
RBox.pack(todosFilter)["<*>"](filterBox)["<*>"](todosBox)
: // If filtering is not needed, just return todosBox as is
todosBox
)
Overview of the Design
-
Task Management (
todosBox
) A global state for managing the entire list of tasks. Adding, deleting, or updating tasks is done through this state. -
Filter Management (
filterBox
) A global state holding the current filter condition (all, completed, incomplete). The tasks displayed are filtered dynamically based on this condition. -
Derived State (
filteredTodosBox
) Dynamically generated list of tasks based on the current filter condition.- If the filter is “all,” it returns the original list (
todosBox
). - Otherwise, it applies
todosFilter
to narrow down the tasks.
- If the filter is “all,” it returns the original list (
5.3 Implementing Each Component
1. Filter Selection
Create Filter.tsx
with the following:
// components/Filter.tsx
// Implementation of the filter selection component
import { useRBox, set } from "f-box-react"
import type { FilterType } from "../state"
import { filterBox } from "../state"
const filterTypes: FilterType[] = ["all", "completed", "incomplete"]
function Filter() {
const [filter] = useRBox(filterBox)
const setFilter = set(filterBox)
return (
<div className="filter-container">
{filterTypes.map((type) => (
<button
key={type}
className={`filter-button ${filter === type ? "active" : ""}`}
onClick={() => setFilter(type)}
>
{type.charAt(0).toUpperCase() + type.slice(1)}
button>
))}
div>
)
}
export default Filter
- Uses
filterBox
to change the filter condition. - Applies the
active
class to visually highlight the selected filter.
2. Displaying and Updating Tasks
Create TodoList.tsx
:
// components/TodoList.tsx
// Displaying the list of todos and update logic
import { useRBox, set } from "f-box-react"
import type { Todo } from "../state"
import { todosBox, filteredTodosBox } from "../state"
const toggleComplete = (id: number) => (todo: Todo) =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
const excludeTodo = (id: number) => (todo: Todo) => todo.id !== id
function TodoList() {
const [filteredTodos] = useRBox(filteredTodosBox)
const [todos] = useRBox(todosBox)
const setTodos = set(todosBox)
const handleToggle = (id: number) => setTodos(todos.map(toggleComplete(id)))
const handleDelete = (id: number) => setTodos(todos.filter(excludeTodo(id)))
return (
<ul className="todo-list">
{filteredTodos.map((todo) => (
<li
key={todo.id}
className={`todo-item ${todo.completed ? "completed" : ""}`}
>
<label className="todo-label">
<input
type="checkbox"
className="todo-checkbox"
checked={todo.completed}
onChange={() => handleToggle(todo.id)}
/>
<div>
<div className="todo-text">{todo.text}div>
<div className="todo-date">{todo.date.toLocaleString()}div>
div>
label>
<button
className="trash"
onClick={() => handleDelete(todo.id)}
aria-label="Delete"
>button>
li>
))}
ul>
)
}
export default TodoList
-
Display: Retrieves the current task list from
filteredTodosBox
and displays it as a list. - Update: Uses checkboxes and delete buttons to update the state of tasks.
3. Adding Tasks
Create AddTodo.tsx
:
// components/AddTodo.tsx
// The form for adding new tasks
import { useRBox, set } from "f-box-react"
import { todosBox } from "../state"
const createTodo = (text: string) => ({
id: Date.now(),
text,
completed: false,
date: new Date(),
})
function AddTodo() {
const [text, textBox] = useRBox("")
const setText = set(textBox)
const addTodo = () => {
if (text.trim() === "") return
todosBox.setValue((todos) => [...todos, createTodo(text)])
setText("")
}
return (
<div className="add-todo-container">
<input
type="text"
className="add-todo-input"
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="Add a task..."
/>
<button className="add-todo-button" onClick={addTodo}>
Add
button>
div>
)
}
export default AddTodo
- Manages local input state via
useRBox
. - When adding a new task, attaches the current date and stores it in
todosBox
.
5.4 Overall App Structure
Create App.tsx
:
// App.tsx
// Container component for the entire app
import AddTodo from "./components/AddTodo"
import TodoList from "./components/TodoList"
import Filter from "./components/Filter"
function App() {
return (
<div className="app-container">
<h1 className="app-title">TODO APPh1>
<AddTodo />
<Filter />
<TodoList />
div>
)
}
export default App
5.5 Demo
Try the Demo
For a quick hands-on experience, check out the following link for a live demo of this application: