Shared Atoms

Check out this Counter component ↓

Counter
0

Here's (a simplified version of) the code ↓

import { Atom, useAtom } from '@effect-atom/atom-react'

const countAtom = Atom.make(0)

export function Counter() {
  const [count, setCount] = useAtom(countAtom)

  return (
    <div>
      <div>{count}</div>
      <button onClick={() => setCount((c) => c + 1)}>
        Increment
      </button>
      <button onClick={() => setCount((c) => c - 1)}>
        Decrement
      </button>
    </div>
  )
}

Now, let's see what happens if we have multiple counters on the same page.

import { Counter } from './Counter'

<div>
  <Counter />
  <Counter />
</div>
Counter
0
Counter
0

As you can see, the counters are not independent. This is because they're using the same countAtom.

The Solution: Atom.family

To create multiple independent counters, we need to use Atom.family. This allows us to create a set of identifiable atoms, ensuring we get a stable reference for each key.

import { Atom, useAtom } from '@effect-atom/atom-react'

// Create a family of counter atoms, each identified by a number.
const counterFamily = Atom.family((id: number) =>
  Atom.make(0)
)

export function FamilyCounter({ id }: { id: number }) {
  // Call the counterFamily with the id to get the atom
  const [count, setCount] = useAtom(counterFamily(id))

  return (
    <div>
      <h3>Counter #{id}</h3>
      <div>{count}</div>
      <button onClick={() => setCount((c) => c + 1)}>
        Increment
      </button>
      <button onClick={() => setCount((c) => c - 1)}>
        Decrement
      </button>
    </div>
  )
}

Now when we render multiple counters with different IDs, they maintain independent state.

<div>
  <FamilyCounter id={1} />
  <FamilyCounter id={2} />
  <FamilyCounter id={3} />
</div>
Counter #1
0
Counter #2
0
Counter #3
0

Now, just for fun, here's Counter 3 again! ↓

<FamilyCounter id={3} />
Counter #3
0

Calling the family function with the same ID will return the same atom.