@nomyx/gun-sync
v1.3.0
Published
A lightweight, powerful library for synchronizing application state in real-time using Gun.js.
Downloads
4
Maintainers
Readme
@nomyx/gun-sync
A lightweight, powerful library for synchronizing application state in real-time using Gun.js with React hooks and TypeScript support.
Features
- 🔄 Real-time state synchronization using Gun.js peer-to-peer database
- ⚛️ React hooks for seamless integration with React applications
- 🔐 User authentication with built-in auth lifecycle management
- 👥 Presence tracking to monitor active users
- 📝 Collection management with CRUD operations
- 🎯 TypeScript support with full type definitions
- 🌐 Context provider for easy Gun instance sharing
- ⚡ Lightweight with minimal dependencies
Installation
npm install @nomyx/gun-sync gunQuick Start
1. Setup with Context Provider
import React from 'react';
import { GunProvider } from '@nomyx/gun-sync';
import App from './App';
function Root() {
return (
<GunProvider peers={['https://gun-manhattan.herokuapp.com/gun']}>
<App />
</GunProvider>
);
}2. Use Hooks in Components
import React from 'react';
import { useGunState, useGunUser } from '@nomyx/gun-sync';
function MyComponent() {
// Synchronized state
const [message, setMessage] = useGunState('chat', 'currentMessage', '');
// User authentication
const [userState, userActions] = useGunUser();
return (
<div>
<input
value={message}
onChange={(e) => setMessage(e.target.value)}
placeholder="Type a message..."
/>
{userState.isLoggedIn ? (
<button onClick={userActions.signOut}>Sign Out</button>
) : (
<button onClick={() => userActions.signIn('username', 'password')}>
Sign In
</button>
)}
</div>
);
}API Reference
Core Functions
createSync(config: SyncConfig): IGunInstance
Creates and initializes a Gun instance with the specified peer configuration.
import { createSync } from '@nomyx/gun-sync';
const gun = createSync({
peers: ['https://gun-manhattan.herokuapp.com/gun']
});Context
<GunProvider peers={string[]}>
Provides a Gun instance to all descendant components.
<GunProvider peers={['wss://gun-us.herokuapp.com/gun']}>
<App />
</GunProvider>useGun(): IGunInstance
Accesses the Gun instance from context.
const gun = useGun();State Management Hooks
useGunState<T>(scope: string, key: string, initialValue: T, gunInstance?: IGunInstance)
Synchronizes a piece of state with Gun.js, similar to useState.
const [count, setCount] = useGunState('app', 'counter', 0);
// Update state
setCount(count + 1);
// Or with function
setCount(prev => prev + 1);useGunValue<T>(scope: string, key: string, initialValue: T, gunInstance?: IGunInstance)
Read-only access to synchronized values.
const currentUser = useGunValue('app', 'currentUser', null);useGunSet<T>(scope: string, gunInstance?: IGunInstance)
Manages a synchronized collection of items.
const [items, { addItem, updateItem, removeItem }] = useGunSet<Todo>('todos');
// Add new item (returns generated ID)
const id = addItem({ text: 'Learn Gun.js', completed: false });
// Update item
updateItem(id, { completed: true });
// Remove item
removeItem(id);Authentication
useGunUser(gunInstance?: IGunInstance)
Manages user authentication lifecycle.
const [userState, userActions] = useGunUser();
// User state
userState.isLoggedIn; // boolean
userState.isLoading; // boolean
userState.user; // user object or null
userState.error; // error message or undefined
// User actions
await userActions.signUp('alice', 'password123');
await userActions.signIn('alice', 'password123');
userActions.signOut();Presence Tracking
useGunPresence(scope: string, gunInstance?: IGunInstance)
Tracks active users within a scope.
const activeUsers = useGunPresence('chat-room-1');
// Returns array of PresenceInfo objects
activeUsers.forEach(user => {
console.log(`User ${user.id} last seen: ${new Date(user.lastSeen)}`);
});Advanced Usage
Without Context Provider
You can use hooks with a specific Gun instance:
import { createSync, useGunState } from '@nomyx/gun-sync';
function MyComponent() {
const gun = createSync({ peers: ['https://gun-manhattan.herokuapp.com/gun'] });
const [data, setData] = useGunState('app', 'data', null, gun);
return <div>{data}</div>;
}Type Safety
All hooks support TypeScript generics for type safety:
interface User {
id: string;
name: string;
email: string;
}
const [users, userActions] = useGunSet<User>('users');
const [currentUser, setCurrentUser] = useGunState<User | null>('app', 'currentUser', null);Examples
Real-time Chat
import React, { useState } from 'react';
import { useGunSet, useGunUser } from '@nomyx/gun-sync';
interface Message {
text: string;
author: string;
timestamp: number;
}
function Chat() {
const [messages, { addItem }] = useGunSet<Message>('chat-messages');
const [userState] = useGunUser();
const [newMessage, setNewMessage] = useState('');
const sendMessage = () => {
if (newMessage.trim() && userState.user) {
addItem({
text: newMessage,
author: userState.user.alias,
timestamp: Date.now()
});
setNewMessage('');
}
};
return (
<div>
<div>
{messages
.sort((a, b) => a.timestamp - b.timestamp)
.map(msg => (
<div key={msg.id}>
<strong>{msg.author}:</strong> {msg.text}
</div>
))
}
</div>
{userState.isLoggedIn && (
<div>
<input
value={newMessage}
onChange={(e) => setNewMessage(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && sendMessage()}
/>
<button onClick={sendMessage}>Send</button>
</div>
)}
</div>
);
}Collaborative Todo List
import React from 'react';
import { useGunSet } from '@nomyx/gun-sync';
interface Todo {
text: string;
completed: boolean;
createdAt: number;
}
function TodoList() {
const [todos, { addItem, updateItem, removeItem }] = useGunSet<Todo>('todos');
const addTodo = (text: string) => {
addItem({
text,
completed: false,
createdAt: Date.now()
});
};
const toggleTodo = (id: string) => {
const todo = todos.find(t => t.id === id);
if (todo) {
updateItem(id, { completed: !todo.completed });
}
};
return (
<div>
<input
placeholder="Add todo..."
onKeyPress={(e) => {
if (e.key === 'Enter') {
addTodo(e.currentTarget.value);
e.currentTarget.value = '';
}
}}
/>
<ul>
{todos.map(todo => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(todo.id)}
/>
<span style={{
textDecoration: todo.completed ? 'line-through' : 'none'
}}>
{todo.text}
</span>
<button onClick={() => removeItem(todo.id)}>Delete</button>
</li>
))}
</ul>
</div>
);
}Peer Dependencies
- React >= 16.8.0
- Gun.js ^0.2020.1239
License
ISC
Contributing
Issues and pull requests are welcome! Please check out the GitHub repository.
Support
For questions and support, please visit the Gun.js community chat.
