Documentation
¶
Overview ¶
Package state provides global state management with fine-grained reactivity for GoWebComponents.
This package implements atom-based state management inspired by SolidJS and Jotai, allowing components to subscribe to global state that persists across the entire application and automatically triggers re-renders when values change.
Basic usage:
import "github.com/monstercameron/GoWebComponents/state"
// In any component
func UserProfile() ui.Node {
username := state.UseAtom("currentUser", "Guest")
login := ui.UseEvent(func() {
username.Set("John Doe")
})
return html.Div(html.Props{},
html.H1(html.Props{}, html.Text(fmt.Sprintf("Welcome, %s", username.Get()))),
html.Button(html.Props{OnClick: login}, html.Text("Login")),
)
}
// In a different component - shares the same state!
func NavBar() ui.Node {
username := state.UseAtom("currentUser", "Guest")
return html.Nav(html.Props{}, html.Span(html.Props{}, html.Text(username.Get())))
}
Key features:
- Global state accessible from any component by ID
- Automatic subscription and re-rendering when state changes
- Type-safe with Go generics
- Thread-safe for concurrent access
- Fine-grained reactivity - only subscribed components re-render
Atoms vs Component State:
- Use state.UseAtom for data that needs to be shared across components
- Use ui.UseState for local component state
- Atoms persist across component unmounts
- Atoms trigger updates in all subscribed components
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Atom ¶
type Atom[T any] struct { // contains filtered or unexported fields }
func UseAtom ¶
UseAtom provides SolidJS-style fine-grained reactivity with global atoms. Atoms are accessible from anywhere in the component tree by ID and automatically trigger re-renders in all subscribed components when updated.
The first component to call UseAtom with a specific ID initializes the atom with the provided initial value. Subsequent calls from other components will use the existing value and subscribe to updates.
Type parameter T can be any Go type. The hook uses generic type parameters for type safety.
Returns:
- A getter function that returns the current atom value
- A setter function that updates the atom and triggers re-renders in all subscribers
Example - Theme Management:
// In theme switcher component
func ThemeSwitcher(props dom.Attrs) *fiber.Element {
theme, setTheme := state.UseAtom("appTheme", "light")
toggle := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
if theme() == "light" {
setTheme("dark")
} else {
setTheme("light")
}
return nil
})
return dom.Button(map[string]interface{}{"onclick": toggle},
fmt.Sprintf("Switch to %s mode", theme()))
}
// In header component - automatically updates when theme changes
func Header(props dom.Attrs) *fiber.Element {
theme, _ := state.UseAtom("appTheme", "light")
bgColor := "white"
if theme() == "dark" {
bgColor = "#333"
}
return dom.Header(map[string]interface{}{
"style": map[string]string{"background-color": bgColor},
}, dom.H1(nil, "My App"))
}
Example - User Authentication:
type User struct {
ID int
Username string
Email string
}
func LoginButton(props dom.Attrs) *fiber.Element {
user, setUser := state.UseAtom("currentUser", User{})
login := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
setUser(User{ID: 1, Username: "johndoe", Email: "john@example.com"})
return nil
})
if user().ID == 0 {
return dom.Button(map[string]interface{}{"onclick": login}, "Login")
}
return dom.Span(nil, fmt.Sprintf("Welcome, %s", user().Username))
}
Thread Safety: UseAtom is thread-safe and can be safely called from multiple goroutines. The internal atom registry uses mutex-based synchronization.
Cleanup: When a component unmounts, it is automatically unsubscribed from all atoms to prevent memory leaks and unnecessary updates.
Best Practices:
- Use descriptive atom IDs (e.g., "currentUser", "appTheme", "shoppingCart")
- Initialize atoms with appropriate default values
Consider using structured types (structs) for complex state
- Avoid storing large amounts of data in atoms (use for coordination, not caching)
Example ¶
package main
import (
"fmt"
"github.com/monstercameron/GoWebComponents/state"
)
func main() {
// Note: UseAtom must be used inside a component
// Access or create a global atom named "user" with default value "Guest"
user := state.UseAtom("user", "Guest")
// Get current value
fmt.Printf("Current user: %s\n", user.Get())
// Update value - this will trigger updates in all components using this atom
user.Set("Alice")
}
Output: