useRBox Guide
useRBox is a powerful React hook provided by F-Box that simplifies reactive state management. By integrating the RBox abstraction with React’s lifecycle, useRBox enables seamless handling of dynamic and dependent states with a declarative approach.
Why Choose useRBox?
useRBox is optimized for reactive and dynamic states that change based on user interactions or external events. It ensures:
- Automatic state synchronization across components.
- Declarative dependency management using operators like
<$>and<*>. - Type-safe reactivity with strong TypeScript support.
- Integration with React’s
useSyncExternalStorefor efficient updates.
For static or immutable states, consider using useBox instead.
Key Benefits of useRBox
- Reactive State Synchronization: Automatically updates components when state changes.
- Declarative Dependencies: Use functional operators to manage dependencies and transformations.
- Global and Local State: Manage both shared state and component-specific state with ease.
- Integration with F-Box: Combine
useRBoxwith other F-Box abstractions likeTaskandEitherfor advanced use cases.
Practical Use Cases
1. Managing Reactive State
Use useRBox to manage state that reacts to user interactions or external events.
Code Example
import { useRBox, set } from "f-box-react"
function Counter() { const [count, countBox] = useRBox(0) const setCount = set(countBox)
return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> <button onClick={() => setCount(count - 1)}>Decrement</button> </div> )}
export default CounterExplanation
countBoxacts as a reactive state encapsulating the count value.- The
setfunction, which is curried, updates the value reactively. - Components using
useRBoxautomatically re-render when the state changes.
2. Derived Reactive State
Create derived states using the map operator <$> to transform RBox values.
Code Example
import { useRBox, set } from "f-box-react"
function DerivedStateExample() { const [base, baseBox] = useRBox(10) const setBase = set(baseBox)
const [double] = useRBox(() => baseBox["<$>"]((x) => x * 2)) const [triple] = useRBox(() => baseBox["<$>"]((x) => x * 3))
return ( <div> <p>Base: {base}</p> <p>Double: {double}</p> <p>Triple: {triple}</p> <button onClick={() => setBase(base + 1)}>Base Increment</button> </div> )}
export default DerivedStateExampleExplanation
baseBoxholds the base value.- Derived states
doubleandtripleare computed reactively using the<$>operator. - Any change to
baseBoxpropagates automatically to derived states.
3. Combining Reactive States
Combine multiple RBox instances using <*> to create complex derived states.
Code Example
import { RBox } from "f-box-core"import { useRBox, set } from "f-box-react"
function CombinedStateExample() { const [x, xBox] = useRBox(5) const [y, yBox] = useRBox(10) const setX = set(xBox) const setY = set(yBox)
const [sum] = useRBox(() => RBox.pack((a: number) => (b: number) => a + b) ["<*>"](xBox) ["<*>"](yBox) )
return ( <div> <p>X: {x}</p> <p>Y: {y}</p> <p>Sum: {sum}</p> <button onClick={() => setX(x + 1)}>X Increment</button> <button onClick={() => setY(y + 1)}>Y Increment</button> </div> )}
export default CombinedStateExampleExplanation
xBoxandyBoxencapsulate independent reactive values.- A curried addition function
(a) => (b) => a + bis combined with the twoRBoxvalues using<*>. - Changes to either
xBoxoryBoxautomatically trigger an update tosum.
4. Combining Task.tryCatch and Either for Error Handling
Handle asynchronous tasks and errors declaratively with Task.tryCatch and Either.
Code Example
import { useRBox, set } from "f-box-react"import { Task, Either } from "f-box-core"
export type Data = { id: number; body: string }
function fetchDataTask() { return Task.tryCatch( () => fetch("https://api.example.com/data") .then((res) => { if (!res.ok) { throw new Error("Failed to fetch data") } return res.json() }) .then((data) => Either.right(data)), (error) => Either.left(error.message) )}
function DataFetcher() { const [data, dataBox] = useRBox(() => Either.left("Loading...")) const setData = set(dataBox)
const loadData = () => fetchDataTask().run().then(setData)
return ( <div> <button onClick={loadData}>Load Data</button> <div> {data.match( (error) => ( <p>Error: {error}</p> ), (data) => ( <p>Data: {JSON.stringify(data)}</p> ) )} </div> </div> )}
export default DataFetcherExplanation
Task.tryCatchwraps asynchronous operations and gracefully handles errors.- Successful results are wrapped in
Either.right, while errors are wrapped inEither.left. - Changes in
dataBoxautomatically propagate to the component usinguseRBox.
5. Global Reactive State Management
Use useRBox to manage dynamic global state, such as themes and user settings, across an entire application.
Code Example
import { RBox } from "f-box-core"
// Define global statesexport const themeBox = RBox.pack("light")
export const settingsBox = RBox.pack({ language: "en", notificationsEnabled: true,})import { useRBox, set } from "f-box-react"import { themeBox } from "./globalState"
function ThemeToggle() { const [theme] = useRBox(themeBox) const setTheme = set(themeBox)
const toggleTheme = () => setTheme(theme === "light" ? "dark" : "light")
return ( <div> <p>Current Theme: {theme}</p> <button onClick={toggleTheme}> Switch to {theme === "light" ? "Dark" : "Light"} Mode </button> </div> )}
export default ThemeToggleimport { useRBox, set } from "f-box-react"import { settingsBox } from "./globalState"
function SettingsManager() { const [settings] = useRBox(settingsBox) const setSettings = set(settingsBox)
const toggleNotifications = () => setSettings({ ...settings, notificationsEnabled: !settings.notificationsEnabled, })
const changeLanguage = (lang: string) => setSettings({ ...settings, language: lang })
return ( <div> <h2>App Settings</h2> <p>Language: {settings.language}</p> <button onClick={() => changeLanguage("en")}>English</button> <button onClick={() => changeLanguage("ja")}>日本語</button>
<p> Notifications: {settings.notificationsEnabled ? "Enabled" : "Disabled"} </p> <button onClick={toggleNotifications}> {settings.notificationsEnabled ? "Disable" : "Enable"} Notifications </button> </div> )}
export default SettingsManagerimport React from "react"import ThemeToggle from "./ThemeToggle"import SettingsManager from "./SettingsManager"
function App() { return ( <div> <h1>Global State Management with useRBox</h1> <ThemeToggle /> <SettingsManager /> </div> )}
export default AppExplanation
-
Centralized State:
themeBoxandsettingsBoxare defined as globalRBoxinstances.- These states are shared across the application and accessed via
useRBox.
-
Reactive Updates:
- Components use
useRBoxto consume and update global states reactively. - Any updates using
setare propagated to all dependent components automatically.
- Components use
-
Separation of Concerns:
- State definitions are centralized in a
globalState.tsfile. - UI components like
ThemeToggleandSettingsManagerare clean and focused only on rendering and interacting with the states.
- State definitions are centralized in a
-
Practical Use:
- ThemeToggle: Switches between light and dark modes globally.
- SettingsManager: Manages user preferences like language and notification settings.
Best Practices
-
Focus on Reactive State: Use
useRBoxfor dynamic states that require reactivity and real-time updates. For purely static values, useuseBox. -
Leverage Functional Operators: Use
<$>for state transformations and<*>for combining states. These operators provide a clean, composable way to manage derived states. -
Centralize Shared State: Place global
RBoxstates in a dedicated file (e.g.,globalState.ts) for consistency and modularity. -
Avoid Direct Mutations: Use the curried
setfunction to updateRBoxvalues, ensuring immutability and predictable updates. -
Error Handling with
Either: PairuseRBoxwithEitherfor declarative handling of success and error states, especially when dealing with asynchronous operations. -
Split Large States: Avoid combining unrelated states into a single
RBox. Instead, split large state objects into smaller, logical parts for better performance and maintainability. -
Decouple Components: Keep components focused on rendering and interaction. State logic and management should be handled outside the UI components.
Common Pitfalls
-
Overusing Dependency Arrays: Avoid manually managing
deps(dependency arrays) inuseRBox. Instead, use functional operators like<$>and<*>to express dependencies declaratively. -
Duplicating Global State: Do not create multiple instances of the same global
RBox. Always import the shared state from a single source. -
Mutating Values Directly: Avoid directly mutating values inside an
RBox. Usesetor other functional transformations to update values reactively. -
Overloading State: Avoid combining too many unrelated values into one
RBox. It makes the state harder to manage and can cause unnecessary re-renders. -
Ignoring Cleanup: While
useRBoxautomatically handles subscriptions, ensure you clean up manually when directly usingRBox.subscribe.
Conclusion
useRBox is a powerful and declarative solution for managing reactive and dynamic state in React applications. By leveraging F-Box’s functional programming features, such as composable operators and strong immutability guarantees, developers can build clean, scalable, and maintainable components. Whether you are managing complex dependencies, derived states, or global reactivity, useRBox seamlessly integrates into your React projects to enhance state management.
Next Steps
Explore related guides and references to deepen your understanding of F-Box:
- useBox Guide: Manage static and derived state efficiently using
useBox. - useRBoxForm Guide: Simplify form handling and validation with
useRBoxForm.