# � FX React Plugin - Complete Cheat Sheet
## <� **Plugin Overview**
**FX React** provides seamless integration between React and FX, offering reactive hooks that automatically sync with FX nodes. No Redux, no context drilling, no manual state management - just pure reactive React with FX.
### **Core Features**
- **Reactive Hooks** - `useFX`, `useFXState` auto-update with FX nodes
- **Zero Boilerplate** - No actions, reducers, or dispatchers needed
- **Form Management** - `useFXForm` with validation and FX sync
- **API Integration** - `useFXAPI` with loading/error states
- **Lifecycle Hooks** - Component registration with FX
- **Caching Support** - `useFXCache` with FX cache integration
- **Context Provider** - `FXProvider` for React component trees
---
## =� **Installation & Setup**
### **Loading the Plugin**
```html
```
### **React Component Setup**
```tsx
import React from 'react';
// Hooks are available globally or via import
const { useFX, useFXState } = FXReact;
function MyApp() {
// FX node automatically updates component
const appTitle = useFX('app.title', 'Default Title');
return
{appTitle} ;
}
```
---
## <� **Core Hooks Reference**
### **useFX - Reactive State Reading**
```tsx
const value = useFX(path, defaultValue)
```
| Parameter | Type | Description |
|-----------|------|-------------|
| `path` | `string` | FX node path |
| `defaultValue` | `any` | Default value if node doesn't exist |
**Examples:**
```tsx
function UserProfile() {
// Basic usage
const userName = useFX('user.name', 'Anonymous');
const userEmail = useFX('user.email');
// Complex objects
const userProfile = useFX('user.profile', {
avatar: '/default-avatar.png',
bio: 'No bio available'
});
// Arrays
const notifications = useFX('user.notifications', []);
return (
{userName}
{userEmail}
{userProfile.bio}
{notifications.length} notifications
);
}
```
### **useFXState - Two-Way Reactive Binding**
```tsx
const [value, setValue] = useFXState(path, defaultValue)
```
**Examples:**
```tsx
function SettingsForm() {
const [username, setUsername] = useFXState('settings.username', '');
const [theme, setTheme] = useFXState('settings.theme', 'light');
const [notifications, setNotifications] = useFXState('settings.notifications', true);
return (
);
}
// Changes automatically sync with FX nodes:
// $$('settings.username').get() === current username
// $$('settings.theme').get() === current theme
```
### **useFXSetter - Write-Only Hook**
```tsx
const setValue = useFXSetter(path)
```
**Examples:**
```tsx
function ActionButtons() {
const setCounter = useFXSetter('app.counter');
const setMessage = useFXSetter('app.message');
const setUserStatus = useFXSetter('user.status');
return (
setCounter(0)}>
Reset Counter
setMessage('Hello from React!')}>
Set Message
setUserStatus('active')}>
Set Status Active
);
}
```
### **useFXNode - Direct Node Access**
```tsx
const node = useFXNode(path)
```
**Examples:**
```tsx
function AdvancedComponent() {
const userNode = useFXNode('user.profile');
const handleComplexUpdate = () => {
if (userNode) {
// Direct node operations
userNode.set({
...userNode.get(),
lastActive: Date.now(),
sessionCount: (userNode.get()?.sessionCount || 0) + 1
});
}
};
return (
Update User Session
);
}
```
---
## =� **Form Management with useFXForm**
### **Basic Form Hook**
```tsx
const form = useFXForm(initialValues, options)
```
**Options:**
| Option | Type | Description |
|--------|------|-------------|
| `fxPath` | `string` | FX node to sync form data |
| `validate` | `function` | Validation function |
| `onSubmit` | `function` | Submit handler |
### **Simple Form Example**
```tsx
function ContactForm() {
const form = useFXForm(
{ name: '', email: '', message: '' },
{
fxPath: 'forms.contact',
validate: (values) => {
const errors = {};
if (!values.name) errors.name = 'Name is required';
if (!values.email.includes('@')) errors.email = 'Invalid email';
return errors;
},
onSubmit: async (values) => {
const response = await fetch('/api/contact', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(values)
});
if (!response.ok) throw new Error('Failed to submit');
alert('Message sent!');
}
}
);
return (
);
}
```
### **Advanced Form with Validation**
```tsx
function UserRegistrationForm() {
const form = useFXForm(
{
username: '',
email: '',
password: '',
confirmPassword: '',
acceptTerms: false
},
{
fxPath: 'forms.registration',
validate: async (values) => {
const errors = {};
// Username validation
if (!values.username) {
errors.username = 'Username is required';
} else if (values.username.length < 3) {
errors.username = 'Username must be at least 3 characters';
}
// Email validation
if (!values.email) {
errors.email = 'Email is required';
} else if (!/\S+@\S+\.\S+/.test(values.email)) {
errors.email = 'Email is invalid';
} else {
// Check if email exists (async validation)
try {
const response = await fetch('/api/check-email', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: values.email })
});
const data = await response.json();
if (data.exists) {
errors.email = 'Email already exists';
}
} catch (err) {
// Handle validation error
}
}
// Password validation
if (!values.password) {
errors.password = 'Password is required';
} else if (values.password.length < 8) {
errors.password = 'Password must be at least 8 characters';
}
// Confirm password
if (values.password !== values.confirmPassword) {
errors.confirmPassword = 'Passwords do not match';
}
// Terms acceptance
if (!values.acceptTerms) {
errors.acceptTerms = 'You must accept the terms';
}
return errors;
},
onSubmit: async (values) => {
const { confirmPassword, ...submitData } = values;
const response = await fetch('/api/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(submitData)
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.message);
}
// Registration successful
const user = await response.json();
$$('user.current').set(user);
$$('auth.isLoggedIn').set(true);
}
}
);
return (
Create Account
form.setValue('username', e.target.value)}
className={form.errors.username ? 'error' : ''}
/>
{form.errors.username && {form.errors.username} }
form.setValue('email', e.target.value)}
className={form.errors.email ? 'error' : ''}
/>
{form.errors.email && {form.errors.email} }
form.setValue('password', e.target.value)}
className={form.errors.password ? 'error' : ''}
/>
{form.errors.password && {form.errors.password} }
form.setValue('confirmPassword', e.target.value)}
className={form.errors.confirmPassword ? 'error' : ''}
/>
{form.errors.confirmPassword && {form.errors.confirmPassword} }
{form.isSubmitting ? 'Creating Account...' : 'Create Account'}
{form.errors.submit && (
Registration failed: {form.errors.submit}
)}
);
}
```
---
## < **API Integration with useFXAPI**
### **Basic API Hook**
```tsx
const { data, loading, error } = useFXAPI(url, options)
```
**Options:**
| Option | Type | Description |
|--------|------|-------------|
| `method` | `string` | HTTP method (GET, POST, etc.) |
| `body` | `any` | Request body |
| `headers` | `object` | Request headers |
| `dependencies` | `array` | Re-fetch when dependencies change |
### **GET Request Examples**
```tsx
function UserList() {
// Simple GET request
const { data: users, loading, error } = useFXAPI('/api/users');
if (loading) return Loading users...
;
if (error) return Error: {error.message}
;
return (
{users?.map(user => (
{user.name} - {user.email}
))}
);
}
function UserDetails() {
const currentUserId = useFX('ui.selectedUserId');
// Re-fetch when user ID changes
const { data: user, loading, error } = useFXAPI(`/api/users/${currentUserId}`, {
dependencies: [currentUserId]
});
if (!currentUserId) return Select a user to view details
;
if (loading) return Loading user details...
;
if (error) return Error loading user: {error.message}
;
return (
{user.name}
Email: {user.email}
Joined: {new Date(user.createdAt).toLocaleDateString()}
);
}
```
### **POST Request Examples**
```tsx
function CreateUserForm() {
const [formData, setFormData] = useFXState('forms.createUser', {
name: '',
email: ''
});
const [shouldCreate, setShouldCreate] = React.useState(false);
// POST request triggered by shouldCreate change
const { data: newUser, loading, error } = useFXAPI('/api/users', {
method: 'POST',
body: formData,
headers: { 'Content-Type': 'application/json' },
dependencies: [shouldCreate]
});
const handleSubmit = (e) => {
e.preventDefault();
setShouldCreate(!shouldCreate); // Toggle to trigger API call
};
React.useEffect(() => {
if (newUser) {
// User created successfully
alert(`User ${newUser.name} created!`);
setFormData({ name: '', email: '' }); // Reset form
}
}, [newUser]);
return (
setFormData({ ...formData, name: e.target.value })}
/>
setFormData({ ...formData, email: e.target.value })}
/>
{loading ? 'Creating...' : 'Create User'}
{error && Error: {error.message}
}
);
}
```
### **Dynamic API Requests**
```tsx
function SearchResults() {
const searchQuery = useFX('search.query', '');
const searchFilters = useFX('search.filters', {});
// Build dynamic API endpoint
const searchUrl = React.useMemo(() => {
if (!searchQuery) return null;
const params = new URLSearchParams({
q: searchQuery,
...searchFilters
});
return `/api/search?${params.toString()}`;
}, [searchQuery, searchFilters]);
const { data: results, loading, error } = useFXAPI(searchUrl, {
dependencies: [searchUrl]
});
if (!searchQuery) return Enter a search query
;
if (loading) return Searching...
;
if (error) return Search error: {error.message}
;
return (
Search Results ({results?.length || 0})
{results?.map(result => (
{result.title}
{result.description}
))}
);
}
```
---
## <� **Performance with useFXCache**
### **Basic Caching**
```tsx
const { data, loading, error } = useFXCache(key, factory, options)
```
**Examples:**
```tsx
function ExpensiveCalculation() {
const inputData = useFX('calculation.input', []);
// Cache expensive calculation results
const { data: result, loading, error } = useFXCache(
`calculation-${JSON.stringify(inputData)}`,
async () => {
// Simulate expensive calculation
await new Promise(resolve => setTimeout(resolve, 2000));
return inputData.reduce((sum, num) => sum + Math.pow(num, 2), 0);
},
{
ttl: 5 * 60 * 1000, // Cache for 5 minutes
dependencies: [inputData]
}
);
if (loading) return Calculating...
;
if (error) return Calculation error: {error.message}
;
return (
Calculation Result
Sum of squares: {result}
);
}
function UserStatistics() {
const userId = useFX('ui.currentUserId');
// Cache user statistics
const { data: stats, loading } = useFXCache(
`user-stats-${userId}`,
async () => {
const response = await fetch(`/api/users/${userId}/stats`);
return response.json();
},
{
ttl: 10 * 60 * 1000, // Cache for 10 minutes
namespace: 'user-data',
dependencies: [userId]
}
);
if (loading) return Loading statistics...
;
return (
User Statistics
Posts: {stats?.postsCount}
Comments: {stats?.commentsCount}
Likes: {stats?.likesCount}
);
}
```
---
## = **Component Lifecycle Integration**
### **useFXLifecycle Hook**
```tsx
function ComponentWithLifecycle() {
const isComponentMounted = useFXLifecycle('MyComponent', {
feature: 'user-dashboard',
version: '1.0.0'
});
React.useEffect(() => {
if (isComponentMounted) {
// Component is fully mounted and registered with FX
$$('components.MyComponent.initialized').set(true);
}
return () => {
// Cleanup when component unmounts
$$('components.MyComponent.initialized').set(false);
};
}, [isComponentMounted]);
return (
Component Status: {isComponentMounted ? 'Mounted' : 'Mounting'}
);
}
```
### **Router Integration with useFXRouter**
```tsx
function NavigationComponent() {
const { currentRoute, params, navigate, router } = useFXRouter();
return (
navigate('/home')}>Home
navigate('/profile')}>Profile
navigate('/settings')}>Settings
Current Route: {currentRoute}
{params && Object.keys(params).length > 0 && (
Route Parameters:
{JSON.stringify(params, null, 2)}
)}
);
}
function UserProfilePage() {
const { params } = useFXRouter();
const userId = params.id;
const { data: user, loading } = useFXAPI(`/api/users/${userId}`, {
dependencies: [userId]
});
if (loading) return Loading user profile...
;
return (
{user?.name}'s Profile
Email: {user?.email}
);
}
```
---
## <� **Context & Provider Pattern**
### **Setting up FX Provider**
```tsx
import React from 'react';
import ReactDOM from 'react-dom';
function App() {
const { FXProvider } = FXReact;
return (
);
}
function UserDashboard() {
const { useFXContext } = FXReact;
const fx = useFXContext(); // Access FX instance directly
const userName = useFX('user.name');
return (
Welcome, {userName}!
fx.setPath('user.status', 'active')}>
Set Status Active
);
}
ReactDOM.render( , document.getElementById('root'));
```
### **Higher-Order Component (HOC)**
```tsx
const { withFX } = FXReact;
// Component that will receive FX data as props
function UserProfile({ fxData, setFXData, fx }) {
return (
User Profile
Name: {fxData?.name}
Email: {fxData?.email}
setFXData({ ...fxData, status: 'updated' })}>
Update Status
);
}
// Wrap component with FX data
const ConnectedUserProfile = withFX(UserProfile, 'user.profile');
function App() {
return ;
}
```
---
## <� **Advanced Patterns & Real-World Examples**
### **Master-Detail Pattern**
```tsx
function UserManagement() {
// List of users
const { data: users, loading: usersLoading } = useFXAPI('/api/users');
const [selectedUserId, setSelectedUserId] = useFXState('ui.selectedUserId', null);
// Selected user details
const { data: selectedUser, loading: userLoading } = useFXAPI(
selectedUserId ? `/api/users/${selectedUserId}` : null,
{ dependencies: [selectedUserId] }
);
return (
Users
{usersLoading ? (
Loading users...
) : (
{users?.map(user => (
setSelectedUserId(user.id)}
>
{user.name}
))}
)}
User Details
{!selectedUserId ? (
Select a user to view details
) : userLoading ? (
Loading user details...
) : (
{selectedUser?.name}
Email: {selectedUser?.email}
Role: {selectedUser?.role}
Created: {new Date(selectedUser?.createdAt).toLocaleDateString()}
)}
);
}
```
### **Real-Time Dashboard**
```tsx
function Dashboard() {
// Real-time metrics that update via WebSocket/SSE
const activeUsers = useFX('metrics.activeUsers', 0);
const systemLoad = useFX('metrics.systemLoad', 0);
const errorRate = useFX('metrics.errorRate', 0);
// Historical data from API
const { data: chartData, loading } = useFXAPI('/api/metrics/chart', {
dependencies: [] // Only fetch once
});
// Cached expensive calculations
const { data: insights } = useFXCache(
'dashboard-insights',
async () => {
const response = await fetch('/api/analytics/insights');
return response.json();
},
{ ttl: 5 * 60 * 1000 } // Cache for 5 minutes
);
// Auto-refresh every 30 seconds
React.useEffect(() => {
const interval = setInterval(() => {
$$('metrics.lastUpdate').set(Date.now());
}, 30000);
return () => clearInterval(interval);
}, []);
return (
useFX('metrics.prev.activeUsers', 0) ? 'up' : 'down'}
/>
80 ? 'warning' : 'normal'}
/>
5 ? 'error' : 'normal'}
/>
{loading ? (
Loading charts...
) : (
)}
AI Insights
{insights?.map(insight => (
{insight.title}
{insight.description}
))}
);
}
function MetricCard({ title, value, trend }) {
return (
{title}
{value}
{trend === 'up' && '�'}
{trend === 'down' && '�'}
{trend === 'warning' && '�'}
{trend === 'error' && '=�'}
);
}
```
### **Shopping Cart Example**
```tsx
function ShoppingCart() {
const [cartItems, setCartItems] = useFXState('cart.items', []);
const [isCheckingOut, setIsCheckingOut] = useFXState('cart.checkingOut', false);
// Calculate totals reactively
const subtotal = React.useMemo(() => {
return cartItems.reduce((sum, item) => sum + (item.price * item.quantity), 0);
}, [cartItems]);
const tax = subtotal * 0.1; // 10% tax
const total = subtotal + tax;
const updateQuantity = (itemId, quantity) => {
if (quantity <= 0) {
removeItem(itemId);
return;
}
const updatedItems = cartItems.map(item =>
item.id === itemId ? { ...item, quantity } : item
);
setCartItems(updatedItems);
};
const removeItem = (itemId) => {
const updatedItems = cartItems.filter(item => item.id !== itemId);
setCartItems(updatedItems);
};
const handleCheckout = async () => {
setIsCheckingOut(true);
try {
const response = await fetch('/api/checkout', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
items: cartItems,
total: total
})
});
if (!response.ok) throw new Error('Checkout failed');
const order = await response.json();
// Clear cart and show success
setCartItems([]);
$$('ui.notification').set({
type: 'success',
message: `Order #${order.id} placed successfully!`
});
} catch (error) {
$$('ui.notification').set({
type: 'error',
message: error.message
});
} finally {
setIsCheckingOut(false);
}
};
if (cartItems.length === 0) {
return (
Your cart is empty
Add some items to get started!
);
}
return (
Shopping Cart
{cartItems.map(item => (
{item.name}
${item.price}
updateQuantity(item.id, item.quantity - 1)}>
-
{item.quantity}
updateQuantity(item.id, item.quantity + 1)}>
+
${(item.price * item.quantity).toFixed(2)}
removeItem(item.id)}
>
Remove
))}
Subtotal:
${subtotal.toFixed(2)}
Tax:
${tax.toFixed(2)}
Total:
${total.toFixed(2)}
{isCheckingOut ? 'Processing...' : `Checkout ($${total.toFixed(2)})`}
);
}
```
---
## =� **Best Practices & Anti-Patterns**
### **DO: Best Practices**
```tsx
// Use meaningful FX node paths
const user = useFX('user.profile.current', null);
const settings = useFX('app.settings.ui', {});
// Provide sensible default values
const theme = useFX('ui.theme', 'light');
const notifications = useFX('user.preferences.notifications', true);
// Use useFXState for two-way binding
const [searchQuery, setSearchQuery] = useFXState('search.query', '');
// Cache expensive operations
const { data: expensiveData } = useFXCache('expensive-calc', heavyCalculation, {
ttl: 300000 // 5 minutes
});
// Handle loading and error states
function DataComponent() {
const { data, loading, error } = useFXAPI('/api/data');
if (loading) return ;
if (error) return ;
return ;
}
// Use form validation
const form = useFXForm(initialValues, {
validate: validateForm,
onSubmit: handleSubmit
});
```
### L **DON'T: Anti-patterns**
```tsx
// L Don't use React state for FX-managed data
function BadComponent() {
const [userData, setUserData] = React.useState(null); // Use useFX instead
const user = useFX('user.current'); // This is better
// L Don't manually sync React state with FX
React.useEffect(() => {
setUserData($$('user.current').get()); // Don't do this
}, []);
return {userData?.name}
;
}
// L Don't ignore loading/error states
function BadAPIComponent() {
const { data } = useFXAPI('/api/users'); // Missing loading/error handling
return (
{data.map(user => {user.name} )} {/* Will crash if data is null */}
);
}
// L Don't put complex logic in render
function BadRenderLogic() {
const users = useFX('users.list', []);
return (
{users.map(user => {
// L Don't do complex processing in render
const processedUser = heavyProcessing(user);
const additionalData = fetchSomeData(user.id);
return ;
})}
);
}
// DO: Move logic to useMemo or custom hooks
function GoodRenderLogic() {
const users = useFX('users.list', []);
const processedUsers = React.useMemo(() => {
return users.map(heavyProcessing);
}, [users]);
return (
{processedUsers.map(user => (
))}
);
}
// L Don't create FX nodes in render
function BadFXUsage() {
const handleClick = () => {
// L Creating nodes on every click
$$(`temp.${Date.now()}`).set(data);
};
return Click ;
}
// DO: Use consistent node paths
function GoodFXUsage() {
const setTempData = useFXSetter('temp.clickData');
const handleClick = () => {
setTempData(data);
};
return Click ;
}
```
---
This comprehensive cheat sheet provides everything needed to effectively use FX React, from basic reactive hooks to advanced patterns for real-world applications. The plugin eliminates Redux complexity while providing powerful reactive state management through FX nodes.