Skip to content

Commit

Permalink
feat: add atomTree utility
Browse files Browse the repository at this point in the history
- createAtom
- createAtom.remove
- createAtom.getSubTree
- createAtom.getNodePath
  • Loading branch information
dmaskasky committed Jan 24, 2025
1 parent 1431682 commit bbe0ee8
Show file tree
Hide file tree
Showing 4 changed files with 2,262 additions and 2,350 deletions.
110 changes: 110 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,113 @@ const todoFamily = atomFamily(
### Codesandbox

<CodeSandbox id="huxd4i" />

---

# atomTree

[atomTree](https://github.com/jotaijs/jotai-family/blob/main/src/atomTree.ts) is a tree structure that allows you to create and remove atoms at a given path.

Use `atomTree` when you need a hierarchical way to store atoms, particularly if you expect many potential paths and want to reuse the same atom for repeated paths while also having an easy cleanup mechanism for subtrees.

```js
import { atom } from 'jotai'
import { atomTree } from 'jotai-family'

const tree = atomTree((path) => atom(path.join('-')))

const atomA = tree(['foo', 'bar'])
const atomB = tree(['foo', 'bar'])

// Remove the atom at the given path
tree.remove(['foo', 'bar'])
```

## atomTree

The **atomTree** utility provides a hierarchical way to create, reuse, and remove Jotai atoms. Each atom is associated with a unique path, which is an array of unknown types. When you request the same path multiple times, `atomTree` ensures that the same atom instance is returned. You can also remove a specific atom or an entire subtree of atoms when they are no longer needed.

Use `atomTree` when you anticipate a large number of potential paths and want to:

- **Reuse the same atom** for repeated paths.
- **Clean up** unwanted atoms easily, including entire subtrees.

```js
import { atom } from 'jotai'
import { atomTree } from 'jotai-family'

// Create a tree instance, passing a factory function
// that takes a path array and returns a new atom.
const tree = atomTree((path) => atom(path.join('-')))

// Create or retrieve the atom at ['foo', 'bar']
const atomA = tree(['foo', 'bar'])
const atomB = tree(['foo', 'bar'])

// atomA and atomB are the same instance.
console.log(atomA === atomB) // true

// Remove the atom at ['foo', 'bar']
// (and optionally remove its entire subtree)
tree.remove(['foo', 'bar'])
```

### API

#### Creating the tree

Creates a new hierarchical tree of Jotai atoms. It accepts a **factory** function that receives a path array and returns an atom.

```ts
type Path = string[] // Or any other array type
type AtomType = Atom<unknown>

function atomTree<Path, AtomType>(
initializePathAtom: (path: Path) => AtomType
): {
(path: Path): AtomType
remove(path: Path, removeSubTree?: boolean): void
getSubTree(path: Path): Node<AtomType> | undefined
getNodePath(path: Path): Node<AtomType>[]
}
```

- **`initializePathAtom`**: A function invoked whenever the tree needs to create a new atom. Receives the `path` as an argument and must return a Jotai atom.

The returned function has four main operations:

1. **`tree(path: Path): AtomType`**
Creates (or retrieves) an atom at the specified path. Subsequent calls with the same path return the same atom instance.

2. **`tree.remove(path: Path, removeSubTree = false): void`**
Removes the atom at the specified path. If `removeSubTree` is `true`, all child paths under that path are also removed.

3. **`tree.getSubTree(path: Path): Node<AtomType> | undefined`**
Retrieves the internal node representing the specified path. This is useful for inspecting the tree structure. The node structure is as follows:

```ts
type Node<AtomType> = {
atom?: AtomType
children?: Map<PathSegment, Node<AtomType>>
}
```
4. **`tree.getNodePath(path: Path): Node<AtomType>[]`**
Returns an array of node objects from the root node to the node at the specified path, inclusive.
## Usage Example
```js
import { atom } from 'jotai'
import { atomTree } from 'jotai-family'

const btree = atomTree((path) => atom(`Data for path: ${path}`))

// Create or retrieve the atom at [true, false]
const userAtom = btree([true, false])

console.log(store.get(userAtom)) // 'Data for path: true,false'

// Remove the atom (and optionally remove its subtree)
btree.remove([true,false])
```
Loading

0 comments on commit bbe0ee8

Please sign in to comment.