Skip to content

Cached Models

Use a model cache when unmounting a component should not destroy its model.

This is useful for chats, editor tabs, media players, previews, and detail screens that should reopen with their previous state. A cache is also a lifetime decision: the model will stay in memory until you delete it or clear the cache.

Key The Model

Cached models are stored by key inside a scope. For a chat, the key can be chatId. For editor tabs, it can be a document id. The key should describe the thing the user expects to return to.

tsx
const chatCache = createModelCache<string, Props, ChatModel>();

export const ChatPanel = component({
  cache: chatCache,
  key: (props: Props) => props.chatId,
  model: createChatModel,
  view({ model }) {
    return <div>{model.messages.items.join(", ")}</div>;
  },
});

When ChatPanel unmounts, the cached model stays alive. When the same key appears again in the same scope, React gets the existing model instead of creating a fresh one.

Loading Logic In The Model

The model can use mounted to load data the first time it appears:

tsx
function createChatModel({ key, mounted }: ModelContext<Props, string>) {
  const messages = store({ items: [] as string[] });
  const loadFx = effect(async (chatId: string) => [`loaded:${chatId}`]);

  reaction({
    on: mounted,
    run() {
      void loadFx(key);
    },
  });

  return { loading: loadFx.$pending, messages };
}

For cached models, think carefully about whether mounted should reload every time a view returns or only when the model is first created. If reloads are expensive, keep an additional store like loaded and guard the reaction.

Dispose Deliberately

Caching trades memory for continuity. Delete a model when the user closes the chat, tab, player, or preview for good.

ts
chatCache.delete("support", appScope);
chatCache.clear(appScope);

Deleting a cached model disposes its owner, so reactions and cleanup registered inside the model are detached as well.