Files
gemini-nexus/.agent/skills/vercel-composition-patterns/rules/state-lift-state.md
T
AngeloooooandDraculabo 8ed4593f73 Refactor Gemini model display: filter old versions, group low/high pкo, and improve naming (#114)
* Refactor Gemini model display: filter old versions, group low/high pro, and improve naming

* Fix potential negative minutes and refine Gemini 3 Pro grouping logic based on PR feedback

* The account change process has been sped up

* Address PR review: enhance error message and parameterize polling interval in process handler

* Address PR review: fix potential race condition in database backup logic

* fix: prevent renderer white screen and harden sentry gating

* feat:  localize model labels

* chore: enable hoisted node-linker

* chore: ignore .agent/.claude/.codex in prettier

---------

Co-authored-by: Draculabo <[email protected]>
2026-02-19 19:37:30 +08:00

126 lines
3.2 KiB
Markdown

---
title: Lift State into Provider Components
impact: HIGH
impactDescription: enables state sharing outside component boundaries
tags: composition, state, context, providers
---
## Lift State into Provider Components
Move state management into dedicated provider components. This allows sibling
components outside the main UI to access and modify state without prop drilling
or awkward refs.
**Incorrect (state trapped inside component):**
```tsx
function ForwardMessageComposer() {
const [state, setState] = useState(initialState);
const forwardMessage = useForwardMessage();
return (
<Composer.Frame>
<Composer.Input />
<Composer.Footer />
</Composer.Frame>
);
}
// Problem: How does this button access composer state?
function ForwardMessageDialog() {
return (
<Dialog>
<ForwardMessageComposer />
<MessagePreview /> {/* Needs composer state */}
<DialogActions>
<CancelButton />
<ForwardButton /> {/* Needs to call submit */}
</DialogActions>
</Dialog>
);
}
```
**Incorrect (useEffect to sync state up):**
```tsx
function ForwardMessageDialog() {
const [input, setInput] = useState('');
return (
<Dialog>
<ForwardMessageComposer onInputChange={setInput} />
<MessagePreview input={input} />
</Dialog>
);
}
function ForwardMessageComposer({ onInputChange }) {
const [state, setState] = useState(initialState);
useEffect(() => {
onInputChange(state.input); // Sync on every change 😬
}, [state.input]);
}
```
**Incorrect (reading state from ref on submit):**
```tsx
function ForwardMessageDialog() {
const stateRef = useRef(null);
return (
<Dialog>
<ForwardMessageComposer stateRef={stateRef} />
<ForwardButton onPress={() => submit(stateRef.current)} />
</Dialog>
);
}
```
**Correct (state lifted to provider):**
```tsx
function ForwardMessageProvider({ children }: { children: React.ReactNode }) {
const [state, setState] = useState(initialState);
const forwardMessage = useForwardMessage();
const inputRef = useRef(null);
return (
<Composer.Provider
state={state}
actions={{ update: setState, submit: forwardMessage }}
meta={{ inputRef }}
>
{children}
</Composer.Provider>
);
}
function ForwardMessageDialog() {
return (
<ForwardMessageProvider>
<Dialog>
<ForwardMessageComposer />
<MessagePreview /> {/* Custom components can access state and actions */}
<DialogActions>
<CancelButton />
<ForwardButton /> {/* Custom components can access state and actions */}
</DialogActions>
</Dialog>
</ForwardMessageProvider>
);
}
function ForwardButton() {
const { actions } = use(Composer.Context);
return <Button onPress={actions.submit}>Forward</Button>;
}
```
The ForwardButton lives outside the Composer.Frame but still has access to the
submit action because it's within the provider. Even though it's a one-off
component, it can still access the composer's state and actions from outside the
UI itself.
**Key insight:** Components that need shared state don't have to be visually
nested inside each other—they just need to be within the same provider.