@ahoo-wang/fetcher-react 3.1.9 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +331 -5
- package/README.zh-CN.md +242 -355
- package/dist/index.es.js +258 -234
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/useImmerKeyStorage.d.ts +4 -0
- package/dist/storage/useImmerKeyStorage.d.ts.map +1 -0
- package/dist/storage/useKeyStorage.d.ts +2 -29
- package/dist/storage/useKeyStorage.d.ts.map +1 -1
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -38,12 +38,13 @@ robust data fetching capabilities.
|
|
|
38
38
|
- [useLatest Hook](#uselatest-hook)
|
|
39
39
|
- [useRefs Hook](#userefs-hook)
|
|
40
40
|
- [useKeyStorage Hook](#usekeystorage-hook)
|
|
41
|
+
- [useImmerKeyStorage Hook](#useimmerkeystorage-hook)
|
|
41
42
|
- [Wow Query Hooks](#wow-query-hooks)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
- [useListQuery Hook](#uselistquery-hook)
|
|
44
|
+
- [usePagedQuery Hook](#usepagedquery-hook)
|
|
45
|
+
- [useSingleQuery Hook](#usesinglequery-hook)
|
|
46
|
+
- [useCountQuery Hook](#usecountquery-hook)
|
|
47
|
+
- [useListStreamQuery Hook](#useliststreamquery-hook)
|
|
47
48
|
- [Best Practices](#best-practices)
|
|
48
49
|
- [API Reference](#api-reference)
|
|
49
50
|
- [License](#license)
|
|
@@ -551,6 +552,264 @@ const updateVolume = (newVolume: number) => {
|
|
|
551
552
|
};
|
|
552
553
|
```
|
|
553
554
|
|
|
555
|
+
### useImmerKeyStorage Hook
|
|
556
|
+
|
|
557
|
+
🚀 **Immer-Powered Immutable State Management** - The `useImmerKeyStorage` hook extends `useKeyStorage` by integrating Immer's `produce` function, enabling intuitive "mutable" updates on stored values while maintaining immutability under the hood. Perfect for complex object manipulations with automatic storage synchronization.
|
|
558
|
+
|
|
559
|
+
#### Key Benefits
|
|
560
|
+
|
|
561
|
+
- **Intuitive Mutations**: Write code that looks mutable but produces immutable updates
|
|
562
|
+
- **Deep Object Support**: Effortlessly handle nested objects and arrays
|
|
563
|
+
- **Type Safety**: Full TypeScript support with compile-time error checking
|
|
564
|
+
- **Performance**: Optimized with Immer's structural sharing and minimal re-renders
|
|
565
|
+
- **Automatic Sync**: Changes automatically persist to storage and sync across components
|
|
566
|
+
|
|
567
|
+
#### When to Use
|
|
568
|
+
|
|
569
|
+
Choose `useImmerKeyStorage` over `useKeyStorage` when you need to:
|
|
570
|
+
|
|
571
|
+
- Update nested object properties
|
|
572
|
+
- Perform complex array operations (push, splice, etc.)
|
|
573
|
+
- Make multiple related changes atomically
|
|
574
|
+
- Work with deeply nested data structures
|
|
575
|
+
|
|
576
|
+
```typescript jsx
|
|
577
|
+
import { KeyStorage } from '@ahoo-wang/fetcher-storage';
|
|
578
|
+
import { useImmerKeyStorage } from '@ahoo-wang/fetcher-react';
|
|
579
|
+
|
|
580
|
+
const MyComponent = () => {
|
|
581
|
+
const prefsStorage = new KeyStorage<{
|
|
582
|
+
theme: string;
|
|
583
|
+
volume: number;
|
|
584
|
+
notifications: boolean;
|
|
585
|
+
shortcuts: { [key: string]: string };
|
|
586
|
+
}>({
|
|
587
|
+
key: 'user-prefs'
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
// Without default value - can be null
|
|
591
|
+
const [prefs, updatePrefs, clearPrefs] = useImmerKeyStorage(prefsStorage);
|
|
592
|
+
|
|
593
|
+
return (
|
|
594
|
+
<div>
|
|
595
|
+
<p>Theme: {prefs?.theme || 'default'}</p>
|
|
596
|
+
<button onClick={() => updatePrefs(draft => { draft.theme = 'dark'; })}>
|
|
597
|
+
Switch to Dark Theme
|
|
598
|
+
</button>
|
|
599
|
+
<button onClick={() => updatePrefs(draft => { draft.volume += 10; })}>
|
|
600
|
+
Increase Volume
|
|
601
|
+
</button>
|
|
602
|
+
<button onClick={clearPrefs}>
|
|
603
|
+
Clear Preferences
|
|
604
|
+
</button>
|
|
605
|
+
</div>
|
|
606
|
+
);
|
|
607
|
+
};
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
#### With Default Value
|
|
611
|
+
|
|
612
|
+
```typescript jsx
|
|
613
|
+
const AudioControls = () => {
|
|
614
|
+
const settingsStorage = new KeyStorage<{ volume: number; muted: boolean }>({
|
|
615
|
+
key: 'audio-settings'
|
|
616
|
+
});
|
|
617
|
+
|
|
618
|
+
// With default value - guaranteed to be non-null
|
|
619
|
+
const [settings, updateSettings, resetSettings] = useImmerKeyStorage(
|
|
620
|
+
settingsStorage,
|
|
621
|
+
{ volume: 50, muted: false }
|
|
622
|
+
);
|
|
623
|
+
|
|
624
|
+
return (
|
|
625
|
+
<div>
|
|
626
|
+
<p>Volume: {settings.volume}%</p>
|
|
627
|
+
<button onClick={() => updateSettings(draft => {
|
|
628
|
+
draft.volume = Math.min(100, draft.volume + 10);
|
|
629
|
+
draft.muted = false;
|
|
630
|
+
})}>
|
|
631
|
+
Increase Volume
|
|
632
|
+
</button>
|
|
633
|
+
<button onClick={() => updateSettings(draft => { draft.muted = !draft.muted; })}>
|
|
634
|
+
Toggle Mute
|
|
635
|
+
</button>
|
|
636
|
+
<button onClick={resetSettings}>
|
|
637
|
+
Reset to Default
|
|
638
|
+
</button>
|
|
639
|
+
</div>
|
|
640
|
+
);
|
|
641
|
+
};
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
#### Advanced Usage Patterns
|
|
645
|
+
|
|
646
|
+
##### Batch Updates
|
|
647
|
+
|
|
648
|
+
```typescript jsx
|
|
649
|
+
const updateUserProfile = () => {
|
|
650
|
+
updatePrefs(draft => {
|
|
651
|
+
draft.theme = 'dark';
|
|
652
|
+
draft.notifications = true;
|
|
653
|
+
draft.volume = 75;
|
|
654
|
+
});
|
|
655
|
+
};
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
##### Array Operations
|
|
659
|
+
|
|
660
|
+
```typescript jsx
|
|
661
|
+
const todoStorage = new KeyStorage<{
|
|
662
|
+
todos: Array<{ id: number; text: string; done: boolean }>;
|
|
663
|
+
}>({
|
|
664
|
+
key: 'todos',
|
|
665
|
+
});
|
|
666
|
+
|
|
667
|
+
const [state, updateState] = useImmerKeyStorage(todoStorage, { todos: [] });
|
|
668
|
+
|
|
669
|
+
// Add new todo
|
|
670
|
+
const addTodo = (text: string) => {
|
|
671
|
+
updateState(draft => {
|
|
672
|
+
draft.todos.push({
|
|
673
|
+
id: Date.now(),
|
|
674
|
+
text,
|
|
675
|
+
done: false,
|
|
676
|
+
});
|
|
677
|
+
});
|
|
678
|
+
};
|
|
679
|
+
|
|
680
|
+
// Toggle todo status
|
|
681
|
+
const toggleTodo = (id: number) => {
|
|
682
|
+
updateState(draft => {
|
|
683
|
+
const todo = draft.todos.find(t => t.id === id);
|
|
684
|
+
if (todo) {
|
|
685
|
+
todo.done = !todo.done;
|
|
686
|
+
}
|
|
687
|
+
});
|
|
688
|
+
};
|
|
689
|
+
|
|
690
|
+
// Remove completed todos
|
|
691
|
+
const clearCompleted = () => {
|
|
692
|
+
updateState(draft => {
|
|
693
|
+
draft.todos = draft.todos.filter(todo => !todo.done);
|
|
694
|
+
});
|
|
695
|
+
};
|
|
696
|
+
```
|
|
697
|
+
|
|
698
|
+
##### Nested Object Updates
|
|
699
|
+
|
|
700
|
+
```typescript jsx
|
|
701
|
+
const configStorage = new KeyStorage<{
|
|
702
|
+
ui: { theme: string; language: string };
|
|
703
|
+
features: { [key: string]: boolean };
|
|
704
|
+
}>({
|
|
705
|
+
key: 'app-config',
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
const [config, updateConfig] = useImmerKeyStorage(configStorage, {
|
|
709
|
+
ui: { theme: 'light', language: 'en' },
|
|
710
|
+
features: {},
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
// Update nested properties
|
|
714
|
+
const updateTheme = (theme: string) => {
|
|
715
|
+
updateConfig(draft => {
|
|
716
|
+
draft.ui.theme = theme;
|
|
717
|
+
});
|
|
718
|
+
};
|
|
719
|
+
|
|
720
|
+
const toggleFeature = (feature: string) => {
|
|
721
|
+
updateConfig(draft => {
|
|
722
|
+
draft.features[feature] = !draft.features[feature];
|
|
723
|
+
});
|
|
724
|
+
};
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
##### Conditional Updates with Validation
|
|
728
|
+
|
|
729
|
+
```typescript jsx
|
|
730
|
+
const updateVolume = (newVolume: number) => {
|
|
731
|
+
updateSettings(draft => {
|
|
732
|
+
if (newVolume >= 0 && newVolume <= 100) {
|
|
733
|
+
draft.volume = newVolume;
|
|
734
|
+
draft.muted = false; // Unmute when volume changes
|
|
735
|
+
}
|
|
736
|
+
});
|
|
737
|
+
};
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
##### Returning New Values
|
|
741
|
+
|
|
742
|
+
```typescript jsx
|
|
743
|
+
// Replace entire state
|
|
744
|
+
const resetToFactorySettings = () => {
|
|
745
|
+
updateSettings(() => ({ volume: 50, muted: false }));
|
|
746
|
+
};
|
|
747
|
+
|
|
748
|
+
// Computed updates
|
|
749
|
+
const setMaxVolume = () => {
|
|
750
|
+
updateSettings(draft => ({ ...draft, volume: 100, muted: false }));
|
|
751
|
+
};
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
##### Error Handling
|
|
755
|
+
|
|
756
|
+
```typescript jsx
|
|
757
|
+
const safeUpdate = (updater: (draft: any) => void) => {
|
|
758
|
+
try {
|
|
759
|
+
updatePrefs(updater);
|
|
760
|
+
} catch (error) {
|
|
761
|
+
console.error('Failed to update preferences:', error);
|
|
762
|
+
// Handle error appropriately
|
|
763
|
+
}
|
|
764
|
+
};
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
#### Best Practices
|
|
768
|
+
|
|
769
|
+
##### ✅ Do's
|
|
770
|
+
|
|
771
|
+
- Use for complex object updates and array manipulations
|
|
772
|
+
- Leverage Immer's draft mutations for readable code
|
|
773
|
+
- Combine multiple related changes in a single update call
|
|
774
|
+
- Use default values for guaranteed non-null state
|
|
775
|
+
- Handle errors appropriately in update functions
|
|
776
|
+
|
|
777
|
+
##### ❌ Don'ts
|
|
778
|
+
|
|
779
|
+
- Don't modify the draft parameter directly with assignment (`draft = newValue`)
|
|
780
|
+
- Don't perform side effects inside updater functions
|
|
781
|
+
- Don't rely on reference equality for object comparisons
|
|
782
|
+
- Don't use for simple primitive value updates (use `useKeyStorage` instead)
|
|
783
|
+
|
|
784
|
+
##### Performance Tips
|
|
785
|
+
|
|
786
|
+
- Batch related updates together to minimize storage operations
|
|
787
|
+
- Use functional updates when the new state depends on the previous state
|
|
788
|
+
- Consider using `useCallback` for updater functions if they're recreated frequently
|
|
789
|
+
- Profile your updates if working with very large objects
|
|
790
|
+
|
|
791
|
+
##### TypeScript Integration
|
|
792
|
+
|
|
793
|
+
```typescript jsx
|
|
794
|
+
// Define strict types for better safety
|
|
795
|
+
type UserPreferences = {
|
|
796
|
+
theme: 'light' | 'dark' | 'auto';
|
|
797
|
+
volume: number; // 0-100
|
|
798
|
+
notifications: boolean;
|
|
799
|
+
shortcuts: Record<string, string>;
|
|
800
|
+
};
|
|
801
|
+
|
|
802
|
+
const prefsStorage = new KeyStorage<UserPreferences>({
|
|
803
|
+
key: 'user-prefs',
|
|
804
|
+
});
|
|
805
|
+
|
|
806
|
+
// TypeScript will catch invalid updates
|
|
807
|
+
const [prefs, updatePrefs] = useImmerKeyStorage(prefsStorage);
|
|
808
|
+
|
|
809
|
+
// This will cause a TypeScript error:
|
|
810
|
+
// updatePrefs(draft => { draft.theme = 'invalid'; });
|
|
811
|
+
```
|
|
812
|
+
|
|
554
813
|
## Wow Query Hooks
|
|
555
814
|
|
|
556
815
|
The Wow Query Hooks provide advanced data querying capabilities with built-in state management for conditions,
|
|
@@ -1743,6 +2002,73 @@ const [theme, setTheme] = useKeyStorage(themeStorage, 'light');
|
|
|
1743
2002
|
// theme: string (never null)
|
|
1744
2003
|
```
|
|
1745
2004
|
|
|
2005
|
+
### useImmerKeyStorage
|
|
2006
|
+
|
|
2007
|
+
```typescript
|
|
2008
|
+
// Without default value - can return null
|
|
2009
|
+
function useImmerKeyStorage<T>(
|
|
2010
|
+
keyStorage: KeyStorage<T>,
|
|
2011
|
+
): [
|
|
2012
|
+
T | null,
|
|
2013
|
+
(updater: (draft: T | null) => T | null | void) => void,
|
|
2014
|
+
() => void,
|
|
2015
|
+
];
|
|
2016
|
+
|
|
2017
|
+
// With default value - guaranteed non-null
|
|
2018
|
+
function useImmerKeyStorage<T>(
|
|
2019
|
+
keyStorage: KeyStorage<T>,
|
|
2020
|
+
defaultValue: T,
|
|
2021
|
+
): [T, (updater: (draft: T) => T | null | void) => void, () => void];
|
|
2022
|
+
```
|
|
2023
|
+
|
|
2024
|
+
A React hook that provides Immer-powered immutable state management for a KeyStorage instance. Extends `useKeyStorage` by integrating Immer's `produce` function, allowing intuitive "mutable" updates on stored values while maintaining immutability.
|
|
2025
|
+
|
|
2026
|
+
**Type Parameters:**
|
|
2027
|
+
|
|
2028
|
+
- `T`: The type of value stored in the key storage
|
|
2029
|
+
|
|
2030
|
+
**Parameters:**
|
|
2031
|
+
|
|
2032
|
+
- `keyStorage`: The KeyStorage instance to subscribe to and manage. Should be a stable reference (useRef, memo, or module-level instance)
|
|
2033
|
+
- `defaultValue` _(optional)_: The default value to use when storage is empty. When provided, the hook guarantees the returned value will never be null
|
|
2034
|
+
|
|
2035
|
+
**Returns:**
|
|
2036
|
+
|
|
2037
|
+
A tuple containing:
|
|
2038
|
+
|
|
2039
|
+
- **Current value**: `T | null` (without default) or `T` (with default)
|
|
2040
|
+
- **Update function**: `(updater: (draft: T | null) => T | null | void) => void` - Immer-powered updater function
|
|
2041
|
+
- **Clear function**: `() => void` - Function to remove the stored value
|
|
2042
|
+
|
|
2043
|
+
**Updater Function:**
|
|
2044
|
+
|
|
2045
|
+
The updater function receives a `draft` parameter that can be mutated directly. Immer will produce an immutable update from these mutations. The updater can also return a new value directly or `null` to clear the storage.
|
|
2046
|
+
|
|
2047
|
+
**Examples:**
|
|
2048
|
+
|
|
2049
|
+
```typescript
|
|
2050
|
+
// Basic object updates
|
|
2051
|
+
const [user, updateUser] = useImmerKeyStorage(userStorage);
|
|
2052
|
+
updateUser(draft => {
|
|
2053
|
+
if (draft) {
|
|
2054
|
+
draft.name = 'John';
|
|
2055
|
+
draft.age = 30;
|
|
2056
|
+
}
|
|
2057
|
+
});
|
|
2058
|
+
|
|
2059
|
+
// Array operations
|
|
2060
|
+
const [todos, updateTodos] = useImmerKeyStorage(todosStorage, []);
|
|
2061
|
+
updateTodos(draft => {
|
|
2062
|
+
draft.push({ id: 1, text: 'New todo', done: false });
|
|
2063
|
+
});
|
|
2064
|
+
|
|
2065
|
+
// Returning new values
|
|
2066
|
+
updateTodos(() => [{ id: 1, text: 'Reset todos', done: false }]);
|
|
2067
|
+
|
|
2068
|
+
// Clearing storage
|
|
2069
|
+
updateTodos(() => null);
|
|
2070
|
+
```
|
|
2071
|
+
|
|
1746
2072
|
### useListQuery
|
|
1747
2073
|
|
|
1748
2074
|
```typescript
|