---
title: Containers
description: Learn the basics behind containers in ReArch.
---

# Containers
After capsules, `Container`s are the next high-level piece you should understand in ReArch.
Containers get their name from the fact that they "contain" capsules' data.
Even if you exclusively use Flutter with `flutter_rearch`
(which provides the `RearchBootstrapper` widget over using containers directly),
it is still a good idea to understand how data is handled in the background.


## So What Exactly Is a Container?
In short, containers are just a fancy `HashMap` wrapper.
They map capsules to their internal states and data.
Containers act lazily and cache data where possible,
invoking capsules (remember--capsules are just functions)
to instantiate the container's cache.

Containers provide:
- `read()` to read the current data of some capsules
- `listen()` to *temporarily* listen to changes in a set of capsule(s)

Each implementation may include other methods on containers,
but you can consult the api level reference/documentation to learn more about those particular methods.
They tend to solve very niche/advanced use cases.


## Reading Capsules
<CodeGroup title="Reading Capsules">
  ```dart
  int countCapsule(CapsuleHandle _) => 0;
  int countPlusOneCapsule(CapsuleHandle use) => use(countCapsule) + 1;

  final container = CapsuleContainer();
  final count = container.read(countCapsule);
  final countPlusOne = container.read(countPlusOneCapsule);
  ```

  ```rust
  fn count_capsule(_: CapsuleHandle) -> u8 {
      0
  }

  fn count_plus_one_capsule(CapsuleHandle { mut get, .. }: CapsuleHandle) -> u8 {
      get(count_capsule) + 1
  }

  let container = Container::new();
  let (count, count_plus_one) = container.read((count, count_plus_one));
  ```
</CodeGroup>


### Important Rust-only Information
Notice how we passed a tuple into `read()` above; this is *very* important.
This ensures a *consistent* read amongst capsules, in addition to reducing overhead.
Take the following scenario:

```rust
let count = container.read(count_capsule);
let count_plus_one = container.read(count_plus_one_capsule);
```

Say another thread increments the container's count after that first line;
count would then be 0 and count_plus_one would be 2!
Thus, group all *container* reads together when possible,
even if you might not always need the state of the capsules you are reading!
(This differs from within a capsule's build,
where you are encouraged to conditionally call `get(some_capsule)` in order to skip some rebuilds.)

Also of importance:
- Containers have cheap clones (`Arc` is used internally), so clone away
- Containers are thread safe and are meant to be shared across threads


## Listening to Changes
To *temporarily* listen to capsules, see the following example.
<CodeGroup title="Temporarily Listening to Capsules">
  ```dart
  final container = CapsuleContainer();
  final disposeListener = container.listen((use) => print(use(fooBarCapsule)));

  // When you no longer need to listen to changes:
  disposeListener();
  ```

  ```rust
  let container = Container::new();
  let handle = container.listen(|| (), |get, ()| {
      println!("{}", get(foo_bar_capsule));
  });

  // When you no longer need to listen to changes:
  drop(handle);
  ```
</CodeGroup>

If you want to listen to capsule changes throughout the lifetime of a Container,
*do not use `listen()`*.
Instead, create a non-idempotent capsule (a fancy way to say a capsule that registers a side effect)
and read it once to ensure it has been initialized and is listening.
<CodeGroup title="Indefinitely Listening to Capsules">
  ```dart
  void listener(CapsuleHandle use) {
    // asListener is a no-op side effect that you can use
    // to declare capsules as listeners
    use.asListener();

    // Listen to your data like normal
    print(use(countCapsule));
  }

  // Initialize the listener so it will get updates
  final container = CapsuleContainer();
  container.read(listener);
  ```

  ```rust
  fn listener(CapsuleHandle { mut get, register }: CapsuleHandle) {
      // as_listener is a no-op side effect that you can use
      // to declare capsules as listeners
      register(effects::as_listener());

      // Listen to data like normal
      println!("{}", get(foo_bar_capsule));
  }

  // Initialize the listener so it will get updates
  let container = Container::new();
  container.read(listener);
  ```
</CodeGroup>
