1.3.12 âĒ Published 1 year ago
@bhammond/react-stateful v1.3.12
@bhammond/react-stateful
Framework-agnostic URL-synchronized state management with built-in state sharing between components. Uses signals to share state efficiently between components.
Features
- ð State sharing between components using signals
- ð URL synchronization with component state
- ð§ Browser navigation (back/forward) support
- ðĪ Framework agnostic design
- ðĶ TypeScript included
- ðŠķ Small bundle size (~1KB)
- ðŠ No dependencies
Installation
npm install @bhammond/react-statefulRequirements
- React 16.8+
Basic Usage
import { useQueryState } from '@bhammond/react-stateful';
function SearchComponent() {
const params = new URLSearchParams(window.location.search);
const [query, setQuery] = useQueryState('q', params);
return (
<input
value={query ?? ''}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search..."
/>
);
}State Sharing Between Components
Components using the same key will share state through signals:
import { useQueryState } from '@bhammond/react-stateful';
function SearchInput({ params }) {
const [query, setQuery] = useQueryState('q', params);
return <input value={query ?? ''} onChange={e => setQuery(e.target.value)} />;
}
function SearchResults({ params }) {
const [query] = useQueryState('q', params);
return <div>Results for: {query}</div>;
}
function FilterStatus({ params }) {
const [query] = useQueryState('q', params);
return <div>Current filter: {query || 'None'}</div>;
}
function SearchPage() {
const params = new URLSearchParams(window.location.search);
return (
<div>
<SearchInput params={params} />
<FilterStatus params={params} />
<SearchResults params={params} />
</div>
);
}Complex Objects
The hook works with complex objects and maintains type safety:
interface Filters {
search: string;
category: string;
sortBy: string;
page: number;
}
const DEFAULT_FILTERS: Filters = {
search: '',
category: 'all',
sortBy: 'date',
page: 1
};
function FilterPanel({ params }) {
const [filters, setFilters] = useQueryState<Filters>('filters', params, DEFAULT_FILTERS);
const updateFilter = (key: keyof Filters, value: Filters[keyof Filters]) => {
setFilters(current => ({
...current,
[key]: value,
page: key === 'page' ? value : 1
}));
};
return (
<div>
<input
value={filters.search}
onChange={e => updateFilter('search', e.target.value)}
/>
<select
value={filters.category}
onChange={e => updateFilter('category', e.target.value)}
>
{/* options */}
</select>
</div>
);
}API Reference
useQueryState
function useQueryState<T = string>(
name: string,
params: ParamsInput,
defaultValue?: T
): [T, (newValue: T | ((prev: T) => T)) => void]Parameters
name: string- URL parameter keyparams: ParamsInput- Either:URLParamsLike: An object with aget(key: string): string | nullmethodRecordParams: An object with string or string array values
defaultValue?: T- Optional default value when parameter is not present
Returns
[value, setValue]- A tuple containing the current value and setter function
Framework Integration
React Router
import { useSearchParams } from 'react-router-dom';
import { useQueryState } from '@bhammond/react-stateful';
function SearchComponent() {
const [searchParams] = useSearchParams();
const [query, setQuery] = useQueryState('q', searchParams);
return (
<input
value={query ?? ''}
onChange={(e) => setQuery(e.target.value)}
/>
);
}Custom Implementation
class CustomParams implements URLParamsLike {
private params: Map<string, string>;
constructor() {
this.params = new Map();
}
get(key: string): string | null {
return this.params.get(key) ?? null;
}
set(key: string, value: string): void {
this.params.set(key, value);
}
}
function Component() {
const params = new CustomParams();
const [value, setValue] = useQueryState('key', params);
// ...
}TypeScript Support
Includes TypeScript definitions with full type inference support.
Performance
- Signal-based state sharing
- Selective component updates
- Batched URL updates
- Small bundle size
- No external dependencies
Contributing
Contributions are welcome. Please feel free to submit a Pull Request.
License
MIT
1.3.12
1 year ago
1.3.11
1 year ago
1.3.10
1 year ago
1.3.9
1 year ago
1.3.8
1 year ago
1.3.7
1 year ago
1.3.6
1 year ago
1.3.5
1 year ago
1.3.4
1 year ago
1.3.3
1 year ago
1.3.2
1 year ago
1.3.1
1 year ago
1.2.0
1 year ago
1.1.3
1 year ago
1.1.2
1 year ago
1.1.1
1 year ago
1.1.0
1 year ago
1.0.2
1 year ago
1.0.1
1 year ago
1.0.0
1 year ago