Skip to content

Commit 26daab5

Browse files
committed
Release 1.0.0: adding deleteKey and update Docs
1 parent ae8f8a3 commit 26daab5

9 files changed

Lines changed: 101 additions & 65 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1-
# Change Log
2-
This project adheres to [Semantic Versioning](http://semver.org/).
1+
## 0.0.3
2+
3+
* Add `deleteKey` method.
4+
5+
## 0.0.2
6+
7+
* Moving `getPath` to $store function.
38

49
## 0.0.1
5-
* Separate deepmap from Nanostores.
10+
* Separate deepmap from Nanostores.

README.md

Lines changed: 67 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<img align="right" width="64" height="64" title="Nano Stores logo"
44
src="https://nanostores.github.io/nanostores/logo.svg">
55

6-
Deep maps extension for [Nano Stores](https://github.com/nanostores/nanostores) state manager.
6+
Deep maps extension for [Nano Stores](https://github.com/nanostores/nanostores) state manager. It provides a store to put big nested objects or arrays and change keys by path with fine-grained reactivity.
77

88
## Install
99

@@ -13,95 +13,121 @@ npm install @nanostores/deepmap
1313

1414
## Usage
1515

16-
Import `deepMap` from this package instead of `nanostores` (which no longer has it).
17-
18-
Use `setKey` to create, replace, or delete any value at a specific path.
16+
Import `deepMap` from this package to initialize a store with a deeply nested object or array.
1917

2018
```ts
2119
import { deepMap } from '@nanostores/deepmap'
2220

23-
type StoreProps = {
21+
type StoreType = {
2422
user?: {
2523
name: string
2624
age: number
2725
}
2826
count?: number
27+
items?: { id: string }[]
2928
}
3029

31-
const $store = deepMap<StoreProps>({
30+
const $store = deepMap<StoreType>({
3231
user: {
3332
name: 'Luke',
3433
age: 19,
35-
}
34+
},
3635
count: 0,
3736
})
37+
```
38+
### Path Syntax
3839

39-
// Replaces the value at 'count'
40-
$store.setKey('count', 1) // -> { ...restValues, count: 1 }
40+
`deepMap` provides various methods to interact with your nested state easily without mutating the original object. It supports two path syntaxes:
41+
- **`key.subkey`:** To access a deeply nested property in a object.
42+
- `key[0]`: To access an item in an array by index.
4143

42-
```
43-
Use `updateKey` to merge new data into an existing object. If the target isn't an object, it will be replaced.
44+
### GetKey
45+
46+
Use `getKey` to obtain the current value at a specific path.
4447

4548
```ts
46-
// 'updateKey' merges, keeping 'name' and only changing 'age'
47-
$store.updateKey('user', { age: 42 })
48-
// -> { user: { name: 'Luke', age: 42 }, ... }
49+
$store.getKey('user.name')
50+
// -> "Luke"
4951
```
50-
To delete a property from an object or an item from an array, just set its path to `undefined`.
52+
53+
### SetKey
54+
55+
Use `setKey` to create or replace any value at a specific path.
5156

5257
```ts
53-
// Deletes 'count' from the store
54-
$store.setKey('count', undefined)
55-
// -> { user: { name: 'Luke', age: 42 } }
58+
// Replaces the value at 'count'
59+
$store.setKey('count', 1)
60+
61+
// Note: undefined value is reserved for deletion
5662
```
57-
### Working with Arrays
58-
DeepMap fully supports arrays as the root value or nested within your state. You can use standard array syntax [index] in your paths.
5963

64+
### UpdateKey and Update
65+
66+
Use `updateKey` to merge new data into an existing object or to concatenate items into an array. If the target isn't an object or array, it will simply be replaced. Use `update` to modify the root state object.
6067
```ts
61-
//Before
62-
const $store = deepMap<StoreProps[]>([{}]) // Type Error
68+
$store.updateKey('user.age', 42)
69+
// -> { user: { name: 'Luke', age: 42 }, ... }
70+
71+
// Pushes/concatenates new items to an array
72+
// Note: You need to use the [] syntax to update an array
73+
$store.updateKey('items', [{ id: 'uuid-2' }])
74+
// -> { items: [{ id: 'uuid-1' }, { id: 'uuid-2' }] }
75+
```
76+
77+
### DeleteKey
6378

64-
//After
65-
const $store = deepMap<StoreProps[]>([{}]) // OK
79+
Use `deleteKey` to remove a property from an object. You can also use `setKey` with an `undefined` value.
6680

81+
```ts
82+
// Deletes 'count' from the store
83+
$store.deleteKey('count')
84+
$store.setKey('count', undefined)
6785
```
6886

69-
### TypeScript Support
87+
## TypeScript Support
7088

71-
The package is written in TypeScript and provides autocomplete for all methods and properties.
89+
The package is written in TypeScript and provides autocomplete for all methods and properties, even the nested ones.
7290

7391
```ts
74-
// Define your type with 'type' keyword instead of 'interface'
75-
// for better autocomplete
7692
type UserType = {
7793
id?: string
94+
settings?: { theme: string }
7895
}
7996

80-
const $userT = deepMap<UserType>({})
81-
82-
$userT.setKey('id', 'uuidString') // Suggestion autocomplete
97+
const $user = deepMap<UserType>({})
8398

99+
// Autocomplete will suggest 'id' and 'settings.theme'
100+
$user.setKey('settings.theme', 'dark')
84101
```
85-
Typescript automatically infers the type of the store value.
102+
103+
Typescript automatically infers and validates the type of the value at the specified path:
86104

87105
```ts
88-
type StoreProps = {
89-
count?: number
90-
}
106+
$store.setKey('count', 'randomString')
107+
// Type Error -> 'string' is not assignable to type 'number'
108+
```
91109

92-
const $store = deepMap<StoreProps>({})
110+
## Integrations
93111

94-
$store.setKey('count', 'randomString')
95-
// Type Error -> 'string' is not assignable to 'number'
112+
### React & Preact
96113

97-
// IMPORTANT: an empty path ('') is treated as the root object
98-
$someStore.setKey('', 'randomString') // Replaces the entire store
114+
When using a hook `useStore` you can pass an array of keys for listening to changes.
115+
116+
```tsx
117+
// Reactivity in user branch, any child mutation fire a re-render
118+
const { name } = useStore($store, { keys: ['user']})
119+
120+
// Specific reactivity
121+
122+
const { name } = useStore($store, { keys: ['user.name']}) // Only listen the exact key path
99123
```
100124

125+
If your component only depends on a specific nested value, listen to the exact path. This prevents unnecessary re-renders when sibling properties change.
126+
101127
## License
102128

103129
MIT
104130

105131
## Credits
106132

107-
* [Dan Kozlov](https://github.com/dkzlv) the author of `deepmap()` in [Nano stores](https://github.com/nanostores/nanostores) core.
133+
* [Dan Kozlov](https://github.com/dkzlv) the author of `deepmap()` in [Nano stores](https://github.com/nanostores/nanostores) core.

deep-map/deepmap.test.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,6 @@ describe('Test updateKey', () => {
8282
})
8383
})
8484

85-
8685
describe('Test path functions', () => {
8786
it('Should throw error when path is undefined', () => {
8887
const badCall = () => {
@@ -93,7 +92,6 @@ describe('Test path functions', () => {
9392
})
9493
})
9594

96-
9795
describe('getPath', () => {
9896
const state = {
9997
user: {

deep-map/deepmap.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,18 @@ export type DeepMapStore<T extends BaseDeepMap> = {
7878
value: Partial<FromPathWithIndexSignatureUndefined<T, K>> | undefined,
7979
) => void;
8080

81+
/**
82+
*
83+
* Delete key store value.
84+
*
85+
* ```js
86+
* $settings.deleteKey('visuals.theme');
87+
* ```
88+
*
89+
* @param key The key name. Attributes can be split with a dot `.` and `[]`.
90+
*/
91+
deleteKey: <K extends AllPaths<T>>(key: K) => void;
92+
8193
/**
8294
* Subscribe to store changes and call listener immediately.
8395
*
@@ -148,5 +160,9 @@ export function deepMap<T extends BaseDeepMap>(init?: T): DeepMapStore<T> {
148160
$deepmap.updateKey("", value);
149161
};
150162

163+
$deepmap.deleteKey = <K extends AllPaths<T>>(key: K): void => {
164+
$deepmap.setKey(key, undefined);
165+
};
166+
151167
return $deepmap;
152168
}

deep-map/index.ts

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,3 @@
1-
export {
2-
deepMap,
3-
type DeepMapStore
4-
} from './deepmap.js'
1+
export { deepMap, type DeepMapStore } from "./deepmap.js";
52

6-
7-
export {
8-
getPath,
9-
setPath,
10-
normalizePath
11-
} from './path.js'
3+
export { getPath, setPath, normalizePath, isObject } from "./path.js";

deep-map/path.types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export type AllPaths<
2121
T,
2222
P extends string = '',
2323
MaxDepth extends number = 12
24-
> = T extends null | undefined
24+
> = T extends undefined
2525
? never
2626
: T extends (infer U)[]
2727
?

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@nanostores/deepmap",
3-
"version": "0.0.2",
3+
"version": "1.0.0",
44
"description": "A helper for Nanostores to create deep maps",
55
"keywords": [
66
"store",
@@ -43,7 +43,7 @@
4343
"node": "^20.0.0 || >=22.0.0"
4444
},
4545
"peerDependencies": {
46-
"nanostores": "^1.0.0"
46+
"nanostores": "^1.4.0"
4747
},
4848
"devDependencies": {
4949
"@eslint/js": "^9.39.1",

pnpm-lock.yaml

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tsconfig.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
"compilerOptions": {
33
"target": "es2018",
44
"module": "esnext",
5-
"moduleResolution": "node",
65
"esModuleInterop": true,
76
"skipLibCheck": true,
87
"allowJs": true,

0 commit comments

Comments
 (0)