@muchekechege/pulsesync
v1.2.0
Published
High-performance pull-model sync for React
Downloads
393
Maintainers
Keywords
Readme
PulseSync ⚡️
A high-performance, Pull-Model state synchronization library for React. Specifically engineered for high-frequency data (Trading Bots, WebSockets, Tickers) where traditional React state or "push" models (like standard Redux or MobX) cause UI lag and "data skipping" on mobile devices.
🚀 The Problem: "The Push Bottleneck"
In high-frequency environments (like XAUUSD markets), ticks arrive constantly. Traditional state managers push every update to React:
WebSocket -> Store -> Notify -> React Render -> Browser Paint
On a mobile device, if the "Paint" takes too long, the next tick is blocked. This leads to:
- Skipped Ticks: The WebSocket buffer overflows because the UI thread is stuck.
- UI Stutters: The Garbage Collector (GC) works overtime cleaning up discarded state objects.
- Crashes: Memory pressure builds up until the mobile browser kills the tab.
🛡️ The Solution: "The Pull Model"
PulseSync reverses the flow. It updates a silent internal store instantly ($<0.1ms$) and allows React to pull the data only when the browser is ready to draw a frame:
WebSocket -> Store (Instant Mutation)
Browser Heartbeat -> requestAnimationFrame -> Pull Latest -> React Render
📦 Installation
npm install @muchekechege/pulsesyncusage
1. Create a Store
Define your initial state and initialize the store. This creates a singleton-like instance and a custom hook.
// stores/marketStore.ts
import { createPulseStore } from '@muchekechege/pulsesync';
export const { store, useSync } = createPulseStore({
price: 0,
timeStamp: 0,
isCalculated: false
});
// Helper to update the store outside of the component
export const increment = () => {
store.update({ price: store.getSnapshot().price + 1 });
};
export const decrement = () => {
store.update({ price: store.getSnapshot().price - 1 });
};
2. Silent Updates (High Frequency)
Update the store inside your WebSocket listeners or calculation loops. These updates are "silent"—they do not trigger React's heavy rendering cycle immediately.
// logic/bot.ts
import { store } from './stores/marketStore';
socket.on('message', (data) => {
// Ultra-fast mutation using Object.assign
// Marks state as 'dirty' to trigger a sync on the next frame
store.update({
price: data.p,
timeStamp: data.d
});
});3. Consume in React
Use the useSync hook with a selector. The component will only re-render if the specific slice of data you selected has changed during the last "heartbeat".
import { useSync } from './stores/marketStore';
const PriceDisplay = () => {
// Only pulls 'price'. Ignores updates to 'timeStamp'.
// Default equality is strict (a === b)
const price = useSync(state => state.price);
return <h1>${price.toFixed(2)}</h1>;
};4. Advanced: Custom Equality
For selectors that return arrays or objects, pass a custom equality function as a second argument to useSync to prevent unnecessary renders.
const orders = useSync(
state => state.orders,
(prev, next) => prev.length === next.length // Custom comparison
);🔥 Key Performance Features
- Zero-Allocation Mutations: Internally uses
Object.assignto update the state object. This prevents the creation of millions of temporary objects, keeping the Garbage Collector silent and avoiding mobile "hiccups". - isDirty CPU Shock Absorber: The internal heartbeat loop only notifies listeners if an update was actually triggered via
store.update(). This saves massive CPU cycles during market "quiet" periods. - Global Heartbeat: Uses a single
requestAnimationFrameloop for all active hooks. No matter how many components you have, there is only ever one sync loop running. - Backpressure Handling: If the device heats up or the CPU is throttled, the UI naturally drops from 60fps to 30fps, but the WebSocket logic continues at full speed in the background.
💡 Best Practices
- Leaf Components: Use
useSyncin the smallest possible component. Instead of one large Dashboard hook, use small hooks inPriceLabel,DigitBox, andStatusIcon. - Simple Selectors: Keep your selectors simple (e.g.,
s => s.value). If you need complex derived math, perform it inside thestore.update()phase, not in the UI hook. - Cleanup: PulseSync automatically stops its internal heartbeat when the last component unmounts, ensuring zero memory leaks.
