Removed Zustand storage, cleanup up auth storage IO interfaces, implementations, and usage.
This commit is contained in:
parent
512f45d357
commit
3c65bdb4c6
1 changed files with 863 additions and 1008 deletions
|
|
@ -3,13 +3,13 @@
|
|||
### [Brief](README.md) | [V1 Problems](v1_problems.md) | [V2 Scope](v2_scope.md) | [V2 Tech Stack](v2_web_client.md) | V2 UX | [V2 Auth](v2_auth.md) | [V2 New Spec Impact](v2_new_spec_impact.md)
|
||||
#### [Overview](v2_ux.md) | [Features](v2_ux_features.md) | [Handlers](v2_ux_handlers.md) | [Screenshots](v2_screenshots.md) | Components | [Interfaces](v2_ux_interfaces.md)
|
||||
|
||||
We are developing React components with Mantine, Zustand, and Storybook using a pattern called "Presentational Components" that emphasizes separation of concerns and testability.
|
||||
We are developing React components with Mantine, **`core/mcp/state` stores + `core/react` hooks**, and Storybook using a pattern called "Presentational Components" that emphasizes separation of concerns and testability.
|
||||
|
||||
## The Presentational / Container Split
|
||||
|
||||
The idea is that a component receives everything it needs through props — the data it renders *and* the callbacks it fires when the user interacts. The component itself has no idea where the data came from or what happens when a button is clicked. It just calls `onSave(formData)` and trusts that someone upstream is handling it.
|
||||
|
||||
This creates clean layering: Zustand stores own the state and logic, container components (or hooks) wire stores to presentational components, and the presentational components themselves are pure rendering machines. Storybook targets that bottom layer exclusively.
|
||||
This creates clean layering: **`core/mcp/state` stores** own InspectorClient-derived data; container components or hooks wire stores to presentational components; presentational components are pure rendering machines. Storybook targets that bottom layer exclusively.
|
||||
|
||||
## What Storybook Does
|
||||
|
||||
|
|
@ -47,11 +47,11 @@ This is where the real challenge lives, because React makes it *very* easy to le
|
|||
|
||||
**Props as the complete contract.** If a component needs to know whether a user has permission to delete something, don't pass in the user's role and let the component figure it out. Pass `canDelete: boolean`. The decision logic belongs in the store or a hook, not in the rendering layer. This also makes stories trivial to write — you just set `canDelete` to `true` or `false`.
|
||||
|
||||
**Callbacks over store access.** If a presentational component imports a Zustand store directly, it's no longer testable in isolation without mocking the store. Instead, have a thin wrapper hook or container that does `const { items, addItem } = useStore(...)` and passes those down. The presentational component just sees `items: Item[]` and `onAdd: (item: Item) => void`.
|
||||
**Callbacks over store access.** If a presentational component imports a global store directly, it's no longer testable in isolation without mocking. Instead, use a thin wrapper hook or container that reads from `useXxx()` hooks and passes props down. The presentational component just sees `items: Item[]` and `onAdd: (item: Item) => void`.
|
||||
|
||||
**Derived state lives outside.** Filtering, sorting, computed totals — all of that should happen in Zustand selectors or custom hooks, not inside the component. The component receives the *already filtered* list.
|
||||
**Derived state lives outside.** Filtering, sorting, computed totals — all of that should happen in store selectors or custom hooks, not inside the component. The component receives the *already filtered* list.
|
||||
|
||||
**Local UI state is the exception.** Things like "is this dropdown open" or "which tab is active" are legitimately component-internal. That's fine — that's UI state, not application state. The heuristic is: if the application would need to know about it (say, to restore state on navigation), it belongs in Zustand. If only the component cares, `useState` is appropriate.
|
||||
**Local UI state is the exception.** Things like "is this dropdown open" or "which tab is active" are legitimately component-internal. That's fine — that's UI state, not application state. The heuristic is: if the application would need to know about it (say, to restore state on navigation), it belongs in shared state (stores/hooks). If only the component cares, `useState` is appropriate.
|
||||
|
||||
## Mantine-Specific Considerations
|
||||
|
||||
|
|
@ -63,4 +63,4 @@ It's the most mature choice, but it's not the only one. Ladle is a lighter alter
|
|||
|
||||
For your stack, Storybook is the most well-trodden path and has the best TypeScript support for auto-generating controls from your prop types, which is genuinely useful. It will infer knobs/controls from your interfaces so you can tweak props interactively in the browser.
|
||||
|
||||
The bottom line: your instinct is right. Design your components around a typed model (the props interface), keep decision-making in Zustand, and Storybook becomes the natural place to develop and verify the visual layer independently. The discipline isn't really about Storybook — it's about prop interface design. Storybook just makes it obvious when you've violated the boundary, because a component that reaches outside its props becomes painful to write stories for.
|
||||
The bottom line: your instinct is right. Design your components around a typed model (the props interface), keep decision-making in hooks/stores, and Storybook becomes the natural place to develop and verify the visual layer independently. The discipline isn't really about Storybook — it's about prop interface design. Storybook just makes it obvious when you've violated the boundary, because a component that reaches outside its props becomes painful to write stories for.
|
||||
|
|
|
|||
Loading…
Reference in a new issue