format: apply repo prettier (3.8.1) to the folded skills tree

963 markdown files reformatted with the repository's pinned prettier so
pnpm format:check covers the folded tree like every other repo file.

The formatter's embedded-language pass also normalized code fences
(TS semicolons, closed HTML tags in examples, lowercased CSS hex colors,
one renumbered list that skipped an index). Alphanumeric token deltas vs
the fold commit were audited file-by-file; all are formatter-equivalent
markup normalizations plus the four sanitized skills.
This commit is contained in:
fargo
2026-08-19 14:37:17 -05:00
parent d2eeb64433
commit 1a822493ba
962 changed files with 29594 additions and 27188 deletions
@@ -73,16 +73,10 @@ function Composer({
) : isThread ? (
<AlsoSendToChannelField id={channelId} />
) : null}
{isEditing ? (
<EditActions />
) : isForwarding ? (
<ForwardActions />
) : (
<DefaultActions />
)}
{isEditing ? <EditActions /> : isForwarding ? <ForwardActions /> : <DefaultActions />}
<Footer onSubmit={onSubmit} />
</form>
)
);
}
```
@@ -102,7 +96,7 @@ function ChannelComposer() {
<Composer.Submit />
</Composer.Footer>
</Composer.Frame>
)
);
}
// Thread composer - adds "also send to channel" field
@@ -118,7 +112,7 @@ function ThreadComposer({ channelId }: { channelId: string }) {
<Composer.Submit />
</Composer.Footer>
</Composer.Frame>
)
);
}
// Edit composer - different footer actions
@@ -133,7 +127,7 @@ function EditComposer() {
<Composer.SaveEdit />
</Composer.Footer>
</Composer.Frame>
)
);
}
```
@@ -177,25 +171,21 @@ function Composer({
</Footer>
)}
</form>
)
);
}
```
**Correct: compound components with shared context**
```tsx
const ComposerContext = createContext<ComposerContextValue | null>(null)
const ComposerContext = createContext<ComposerContextValue | null>(null);
function ComposerProvider({ children, state, actions, meta }: ProviderProps) {
return (
<ComposerContext value={{ state, actions, meta }}>
{children}
</ComposerContext>
)
return <ComposerContext value={{ state, actions, meta }}>{children}</ComposerContext>;
}
function ComposerFrame({ children }: { children: React.ReactNode }) {
return <form>{children}</form>
return <form>{children}</form>;
}
function ComposerInput() {
@@ -203,21 +193,21 @@ function ComposerInput() {
state,
actions: { update },
meta: { inputRef },
} = use(ComposerContext)
} = use(ComposerContext);
return (
<TextInput
ref={inputRef}
value={state.input}
onChangeText={(text) => update((s) => ({ ...s, input: text }))}
/>
)
);
}
function ComposerSubmit() {
const {
actions: { submit },
} = use(ComposerContext)
return <Button onPress={submit}>Send</Button>
} = use(ComposerContext);
return <Button onPress={submit}>Send</Button>;
}
// Export as compound component
@@ -231,7 +221,7 @@ const Composer = {
Attachments: ComposerAttachments,
Formatting: ComposerFormatting,
Emojis: ComposerEmojis,
}
};
```
**Usage:**
@@ -275,18 +265,15 @@ useState, Zustand, or a server sync.
```tsx
function ChannelComposer({ channelId }: { channelId: string }) {
// UI component knows about global state implementation
const state = useGlobalChannelState(channelId)
const { submit, updateInput } = useChannelSync(channelId)
const state = useGlobalChannelState(channelId);
const { submit, updateInput } = useChannelSync(channelId);
return (
<Composer.Frame>
<Composer.Input
value={state.input}
onChange={(text) => sync.updateInput(text)}
/>
<Composer.Input value={state.input} onChange={(text) => sync.updateInput(text)} />
<Composer.Submit onPress={() => sync.submit()} />
</Composer.Frame>
)
);
}
```
@@ -298,21 +285,17 @@ function ChannelProvider({
channelId,
children,
}: {
channelId: string
children: React.ReactNode
channelId: string;
children: React.ReactNode;
}) {
const { state, update, submit } = useGlobalChannel(channelId)
const inputRef = useRef(null)
const { state, update, submit } = useGlobalChannel(channelId);
const inputRef = useRef(null);
return (
<Composer.Provider
state={state}
actions={{ update, submit }}
meta={{ inputRef }}
>
<Composer.Provider state={state} actions={{ update, submit }} meta={{ inputRef }}>
{children}
</Composer.Provider>
)
);
}
// UI component only knows about the context interface
@@ -325,7 +308,7 @@ function ChannelComposer() {
<Composer.Submit />
</Composer.Footer>
</Composer.Frame>
)
);
}
// Usage
@@ -334,7 +317,7 @@ function Channel({ channelId }: { channelId: string }) {
<ChannelProvider channelId={channelId}>
<ChannelComposer />
</ChannelProvider>
)
);
}
```
@@ -343,28 +326,25 @@ function Channel({ channelId }: { channelId: string }) {
```tsx
// Local state for ephemeral forms
function ForwardMessageProvider({ children }) {
const [state, setState] = useState(initialState)
const forwardMessage = useForwardMessage()
const [state, setState] = useState(initialState);
const forwardMessage = useForwardMessage();
return (
<Composer.Provider
state={state}
actions={{ update: setState, submit: forwardMessage }}
>
<Composer.Provider state={state} actions={{ update: setState, submit: forwardMessage }}>
{children}
</Composer.Provider>
)
);
}
// Global synced state for channels
function ChannelProvider({ channelId, children }) {
const { state, update, submit } = useGlobalChannel(channelId)
const { state, update, submit } = useGlobalChannel(channelId);
return (
<Composer.Provider state={state} actions={{ update, submit }}>
{children}
</Composer.Provider>
)
);
}
```
@@ -393,8 +373,8 @@ dependency-injectable.
```tsx
function ComposerInput() {
// Tightly coupled to a specific hook
const { input, setInput } = useChannelComposerState()
return <TextInput value={input} onChangeText={setInput} />
const { input, setInput } = useChannelComposerState();
return <TextInput value={input} onChangeText={setInput} />;
}
```
@@ -403,27 +383,27 @@ function ComposerInput() {
```tsx
// Define a GENERIC interface that any provider can implement
interface ComposerState {
input: string
attachments: Attachment[]
isSubmitting: boolean
input: string;
attachments: Attachment[];
isSubmitting: boolean;
}
interface ComposerActions {
update: (updater: (state: ComposerState) => ComposerState) => void
submit: () => void
update: (updater: (state: ComposerState) => ComposerState) => void;
submit: () => void;
}
interface ComposerMeta {
inputRef: React.RefObject<TextInput>
inputRef: React.RefObject<TextInput>;
}
interface ComposerContextValue {
state: ComposerState
actions: ComposerActions
meta: ComposerMeta
state: ComposerState;
actions: ComposerActions;
meta: ComposerMeta;
}
const ComposerContext = createContext<ComposerContextValue | null>(null)
const ComposerContext = createContext<ComposerContextValue | null>(null);
```
**UI components consume the interface, not the implementation:**
@@ -434,7 +414,7 @@ function ComposerInput() {
state,
actions: { update },
meta,
} = use(ComposerContext)
} = use(ComposerContext);
// This component works with ANY provider that implements the interface
return (
@@ -443,7 +423,7 @@ function ComposerInput() {
value={state.input}
onChangeText={(text) => update((s) => ({ ...s, input: text }))}
/>
)
);
}
```
@@ -452,9 +432,9 @@ function ComposerInput() {
```tsx
// Provider A: Local state for ephemeral forms
function ForwardMessageProvider({ children }: { children: React.ReactNode }) {
const [state, setState] = useState(initialState)
const inputRef = useRef(null)
const submit = useForwardMessage()
const [state, setState] = useState(initialState);
const inputRef = useRef(null);
const submit = useForwardMessage();
return (
<ComposerContext
@@ -466,13 +446,13 @@ function ForwardMessageProvider({ children }: { children: React.ReactNode }) {
>
{children}
</ComposerContext>
)
);
}
// Provider B: Global synced state for channels
function ChannelProvider({ channelId, children }: Props) {
const { state, update, submit } = useGlobalChannel(channelId)
const inputRef = useRef(null)
const { state, update, submit } = useGlobalChannel(channelId);
const inputRef = useRef(null);
return (
<ComposerContext
@@ -484,7 +464,7 @@ function ChannelProvider({ channelId, children }: Props) {
>
{children}
</ComposerContext>
)
);
}
```
@@ -534,21 +514,21 @@ function ForwardMessageDialog() {
</DialogActions>
</Dialog>
</ForwardMessageProvider>
)
);
}
// This button lives OUTSIDE Composer.Frame but can still submit based on its context!
function ForwardButton() {
const {
actions: { submit },
} = use(ComposerContext)
return <Button onPress={submit}>Forward</Button>
} = use(ComposerContext);
return <Button onPress={submit}>Forward</Button>;
}
// This preview lives OUTSIDE Composer.Frame but can read composer's state!
function MessagePreview() {
const { state } = use(ComposerContext)
return <Preview message={state.input} attachments={state.attachments} />
const { state } = use(ComposerContext);
return <Preview message={state.input} attachments={state.attachments} />;
}
```
@@ -582,15 +562,15 @@ or awkward refs.
```tsx
function ForwardMessageComposer() {
const [state, setState] = useState(initialState)
const forwardMessage = useForwardMessage()
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?
@@ -604,7 +584,7 @@ function ForwardMessageDialog() {
<ForwardButton /> {/* Needs to call submit */}
</DialogActions>
</Dialog>
)
);
}
```
@@ -612,20 +592,20 @@ function ForwardMessageDialog() {
```tsx
function ForwardMessageDialog() {
const [input, setInput] = useState('')
const [input, setInput] = useState('');
return (
<Dialog>
<ForwardMessageComposer onInputChange={setInput} />
<MessagePreview input={input} />
</Dialog>
)
);
}
function ForwardMessageComposer({ onInputChange }) {
const [state, setState] = useState(initialState)
const [state, setState] = useState(initialState);
useEffect(() => {
onInputChange(state.input) // Sync on every change 😬
}, [state.input])
onInputChange(state.input); // Sync on every change 😬
}, [state.input]);
}
```
@@ -633,13 +613,13 @@ function ForwardMessageComposer({ onInputChange }) {
```tsx
function ForwardMessageDialog() {
const stateRef = useRef(null)
const stateRef = useRef(null);
return (
<Dialog>
<ForwardMessageComposer stateRef={stateRef} />
<ForwardButton onPress={() => submit(stateRef.current)} />
</Dialog>
)
);
}
```
@@ -647,9 +627,9 @@ function ForwardMessageDialog() {
```tsx
function ForwardMessageProvider({ children }: { children: React.ReactNode }) {
const [state, setState] = useState(initialState)
const forwardMessage = useForwardMessage()
const inputRef = useRef(null)
const [state, setState] = useState(initialState);
const forwardMessage = useForwardMessage();
const inputRef = useRef(null);
return (
<Composer.Provider
@@ -659,7 +639,7 @@ function ForwardMessageProvider({ children }: { children: React.ReactNode }) {
>
{children}
</Composer.Provider>
)
);
}
function ForwardMessageDialog() {
@@ -674,12 +654,12 @@ function ForwardMessageDialog() {
</DialogActions>
</Dialog>
</ForwardMessageProvider>
)
);
}
function ForwardButton() {
const { actions } = use(Composer.Context)
return <Button onPress={actions.submit}>Forward</Button>
const { actions } = use(Composer.Context);
return <Button onPress={actions.submit}>Forward</Button>;
}
```
@@ -718,13 +698,7 @@ itself.
```tsx
// What does this component actually render?
<Composer
isThread
isEditing={false}
channelId='abc'
showAttachments
showFormatting={false}
/>
<Composer isThread isEditing={false} channelId="abc" showAttachments showFormatting={false} />
```
**Correct: explicit variants**
@@ -760,7 +734,7 @@ function ThreadComposer({ channelId }: { channelId: string }) {
</Composer.Footer>
</Composer.Frame>
</ThreadProvider>
)
);
}
function EditMessageComposer({ messageId }: { messageId: string }) {
@@ -776,7 +750,7 @@ function EditMessageComposer({ messageId }: { messageId: string }) {
</Composer.Footer>
</Composer.Frame>
</EditMessageProvider>
)
);
}
function ForwardMessageComposer({ messageId }: { messageId: string }) {
@@ -791,7 +765,7 @@ function ForwardMessageComposer({ messageId }: { messageId: string }) {
</Composer.Footer>
</Composer.Frame>
</ForwardMessageProvider>
)
);
}
```
@@ -823,9 +797,9 @@ function Composer({
renderFooter,
renderActions,
}: {
renderHeader?: () => React.ReactNode
renderFooter?: () => React.ReactNode
renderActions?: () => React.ReactNode
renderHeader?: () => React.ReactNode;
renderFooter?: () => React.ReactNode;
renderActions?: () => React.ReactNode;
}) {
return (
<form>
@@ -834,7 +808,7 @@ function Composer({
{renderFooter ? renderFooter() : <DefaultFooter />}
{renderActions?.()}
</form>
)
);
}
// Usage is awkward and inflexible
@@ -849,18 +823,18 @@ return (
)}
renderActions={() => <SubmitButton />}
/>
)
);
```
**Correct: compound components with children**
```tsx
function ComposerFrame({ children }: { children: React.ReactNode }) {
return <form>{children}</form>
return <form>{children}</form>;
}
function ComposerFooter({ children }: { children: React.ReactNode }) {
return <footer className='flex'>{children}</footer>
return <footer className="flex">{children}</footer>;
}
// Usage is flexible
@@ -874,17 +848,14 @@ return (
<SubmitButton />
</Composer.Footer>
</Composer.Frame>
)
);
```
**When render props are appropriate:**
```tsx
// Render props work well when you need to pass data back
<List
data={items}
renderItem={({ item, index }) => <Item item={item} index={index} />}
/>
<List data={items} renderItem={({ item, index }) => <Item item={item} index={index} />} />
```
Use render props when the parent needs to provide data or state to the child.
@@ -911,28 +882,28 @@ In React 19, `ref` is now a regular prop (no `forwardRef` wrapper needed), and `
```tsx
const ComposerInput = forwardRef<TextInput, Props>((props, ref) => {
return <TextInput ref={ref} {...props} />
})
return <TextInput ref={ref} {...props} />;
});
```
**Correct: ref as a regular prop**
```tsx
function ComposerInput({ ref, ...props }: Props & { ref?: React.Ref<TextInput> }) {
return <TextInput ref={ref} {...props} />
return <TextInput ref={ref} {...props} />;
}
```
**Incorrect: useContext in React 19**
```tsx
const value = useContext(MyContext)
const value = useContext(MyContext);
```
**Correct: use instead of useContext**
```tsx
const value = use(MyContext)
const value = use(MyContext);
```
`use()` can also be called conditionally, unlike `useContext()`.
@@ -1,7 +1,6 @@
---
name: vercel-composition-patterns
description:
React composition patterns that scale. Use when refactoring components with
description: React composition patterns that scale. Use when refactoring components with
boolean prop proliferation, building flexible component libraries, or
designing reusable APIs. Triggers on tasks involving compound components,
render props, context providers, or component architecture. Includes React 19
@@ -32,16 +32,10 @@ function Composer({
) : isThread ? (
<AlsoSendToChannelField id={channelId} />
) : null}
{isEditing ? (
<EditActions />
) : isForwarding ? (
<ForwardActions />
) : (
<DefaultActions />
)}
{isEditing ? <EditActions /> : isForwarding ? <ForwardActions /> : <DefaultActions />}
<Footer onSubmit={onSubmit} />
</form>
)
);
}
```
@@ -61,7 +55,7 @@ function ChannelComposer() {
<Composer.Submit />
</Composer.Footer>
</Composer.Frame>
)
);
}
// Thread composer - adds "also send to channel" field
@@ -77,7 +71,7 @@ function ThreadComposer({ channelId }: { channelId: string }) {
<Composer.Submit />
</Composer.Footer>
</Composer.Frame>
)
);
}
// Edit composer - different footer actions
@@ -92,7 +86,7 @@ function EditComposer() {
<Composer.SaveEdit />
</Composer.Footer>
</Composer.Frame>
)
);
}
```
@@ -37,25 +37,21 @@ function Composer({
</Footer>
)}
</form>
)
);
}
```
**Correct (compound components with shared context):**
```tsx
const ComposerContext = createContext<ComposerContextValue | null>(null)
const ComposerContext = createContext<ComposerContextValue | null>(null);
function ComposerProvider({ children, state, actions, meta }: ProviderProps) {
return (
<ComposerContext value={{ state, actions, meta }}>
{children}
</ComposerContext>
)
return <ComposerContext value={{ state, actions, meta }}>{children}</ComposerContext>;
}
function ComposerFrame({ children }: { children: React.ReactNode }) {
return <form>{children}</form>
return <form>{children}</form>;
}
function ComposerInput() {
@@ -63,21 +59,21 @@ function ComposerInput() {
state,
actions: { update },
meta: { inputRef },
} = use(ComposerContext)
} = use(ComposerContext);
return (
<TextInput
ref={inputRef}
value={state.input}
onChangeText={(text) => update((s) => ({ ...s, input: text }))}
/>
)
);
}
function ComposerSubmit() {
const {
actions: { submit },
} = use(ComposerContext)
return <Button onPress={submit}>Send</Button>
} = use(ComposerContext);
return <Button onPress={submit}>Send</Button>;
}
// Export as compound component
@@ -91,7 +87,7 @@ const Composer = {
Attachments: ComposerAttachments,
Formatting: ComposerFormatting,
Emojis: ComposerEmojis,
}
};
```
**Usage:**
@@ -19,9 +19,9 @@ function Composer({
renderFooter,
renderActions,
}: {
renderHeader?: () => React.ReactNode
renderFooter?: () => React.ReactNode
renderActions?: () => React.ReactNode
renderHeader?: () => React.ReactNode;
renderFooter?: () => React.ReactNode;
renderActions?: () => React.ReactNode;
}) {
return (
<form>
@@ -30,7 +30,7 @@ function Composer({
{renderFooter ? renderFooter() : <DefaultFooter />}
{renderActions?.()}
</form>
)
);
}
// Usage is awkward and inflexible
@@ -45,18 +45,18 @@ return (
)}
renderActions={() => <SubmitButton />}
/>
)
);
```
**Correct (compound components with children):**
```tsx
function ComposerFrame({ children }: { children: React.ReactNode }) {
return <form>{children}</form>
return <form>{children}</form>;
}
function ComposerFooter({ children }: { children: React.ReactNode }) {
return <footer className='flex'>{children}</footer>
return <footer className="flex">{children}</footer>;
}
// Usage is flexible
@@ -70,17 +70,14 @@ return (
<SubmitButton />
</Composer.Footer>
</Composer.Frame>
)
);
```
**When render props are appropriate:**
```tsx
// Render props work well when you need to pass data back
<List
data={items}
renderItem={({ item, index }) => <Item item={item} index={index} />}
/>
<List data={items} renderItem={({ item, index }) => <Item item={item} index={index} />} />
```
Use render props when the parent needs to provide data or state to the child.
@@ -15,13 +15,7 @@ itself.
```tsx
// What does this component actually render?
<Composer
isThread
isEditing={false}
channelId='abc'
showAttachments
showFormatting={false}
/>
<Composer isThread isEditing={false} channelId="abc" showAttachments showFormatting={false} />
```
**Correct (explicit variants):**
@@ -56,7 +50,7 @@ function ThreadComposer({ channelId }: { channelId: string }) {
</Composer.Footer>
</Composer.Frame>
</ThreadProvider>
)
);
}
function EditMessageComposer({ messageId }: { messageId: string }) {
@@ -72,7 +66,7 @@ function EditMessageComposer({ messageId }: { messageId: string }) {
</Composer.Footer>
</Composer.Frame>
</EditMessageProvider>
)
);
}
function ForwardMessageComposer({ messageId }: { messageId: string }) {
@@ -87,7 +81,7 @@ function ForwardMessageComposer({ messageId }: { messageId: string }) {
</Composer.Footer>
</Composer.Frame>
</ForwardMessageProvider>
)
);
}
```
@@ -15,28 +15,28 @@ In React 19, `ref` is now a regular prop (no `forwardRef` wrapper needed), and `
```tsx
const ComposerInput = forwardRef<TextInput, Props>((props, ref) => {
return <TextInput ref={ref} {...props} />
})
return <TextInput ref={ref} {...props} />;
});
```
**Correct (ref as a regular prop):**
```tsx
function ComposerInput({ ref, ...props }: Props & { ref?: React.Ref<TextInput> }) {
return <TextInput ref={ref} {...props} />
return <TextInput ref={ref} {...props} />;
}
```
**Incorrect (useContext in React 19):**
```tsx
const value = useContext(MyContext)
const value = useContext(MyContext);
```
**Correct (use instead of useContext):**
```tsx
const value = use(MyContext)
const value = use(MyContext);
```
`use()` can also be called conditionally, unlike `useContext()`.
@@ -20,8 +20,8 @@ dependency-injectable.
```tsx
function ComposerInput() {
// Tightly coupled to a specific hook
const { input, setInput } = useChannelComposerState()
return <TextInput value={input} onChangeText={setInput} />
const { input, setInput } = useChannelComposerState();
return <TextInput value={input} onChangeText={setInput} />;
}
```
@@ -30,27 +30,27 @@ function ComposerInput() {
```tsx
// Define a GENERIC interface that any provider can implement
interface ComposerState {
input: string
attachments: Attachment[]
isSubmitting: boolean
input: string;
attachments: Attachment[];
isSubmitting: boolean;
}
interface ComposerActions {
update: (updater: (state: ComposerState) => ComposerState) => void
submit: () => void
update: (updater: (state: ComposerState) => ComposerState) => void;
submit: () => void;
}
interface ComposerMeta {
inputRef: React.RefObject<TextInput>
inputRef: React.RefObject<TextInput>;
}
interface ComposerContextValue {
state: ComposerState
actions: ComposerActions
meta: ComposerMeta
state: ComposerState;
actions: ComposerActions;
meta: ComposerMeta;
}
const ComposerContext = createContext<ComposerContextValue | null>(null)
const ComposerContext = createContext<ComposerContextValue | null>(null);
```
**UI components consume the interface, not the implementation:**
@@ -61,7 +61,7 @@ function ComposerInput() {
state,
actions: { update },
meta,
} = use(ComposerContext)
} = use(ComposerContext);
// This component works with ANY provider that implements the interface
return (
@@ -70,7 +70,7 @@ function ComposerInput() {
value={state.input}
onChangeText={(text) => update((s) => ({ ...s, input: text }))}
/>
)
);
}
```
@@ -79,9 +79,9 @@ function ComposerInput() {
```tsx
// Provider A: Local state for ephemeral forms
function ForwardMessageProvider({ children }: { children: React.ReactNode }) {
const [state, setState] = useState(initialState)
const inputRef = useRef(null)
const submit = useForwardMessage()
const [state, setState] = useState(initialState);
const inputRef = useRef(null);
const submit = useForwardMessage();
return (
<ComposerContext
@@ -93,13 +93,13 @@ function ForwardMessageProvider({ children }: { children: React.ReactNode }) {
>
{children}
</ComposerContext>
)
);
}
// Provider B: Global synced state for channels
function ChannelProvider({ channelId, children }: Props) {
const { state, update, submit } = useGlobalChannel(channelId)
const inputRef = useRef(null)
const { state, update, submit } = useGlobalChannel(channelId);
const inputRef = useRef(null);
return (
<ComposerContext
@@ -111,7 +111,7 @@ function ChannelProvider({ channelId, children }: Props) {
>
{children}
</ComposerContext>
)
);
}
```
@@ -165,21 +165,21 @@ function ForwardMessageDialog() {
</DialogActions>
</Dialog>
</ForwardMessageProvider>
)
);
}
// This button lives OUTSIDE Composer.Frame but can still submit based on its context!
function ForwardButton() {
const {
actions: { submit },
} = use(ComposerContext)
return <Button onPress={submit}>Forward</Button>
} = use(ComposerContext);
return <Button onPress={submit}>Forward</Button>;
}
// This preview lives OUTSIDE Composer.Frame but can read composer's state!
function MessagePreview() {
const { state } = use(ComposerContext)
return <Preview message={state.input} attachments={state.attachments} />
const { state } = use(ComposerContext);
return <Preview message={state.input} attachments={state.attachments} />;
}
```
@@ -16,18 +16,15 @@ useState, Zustand, or a server sync.
```tsx
function ChannelComposer({ channelId }: { channelId: string }) {
// UI component knows about global state implementation
const state = useGlobalChannelState(channelId)
const { submit, updateInput } = useChannelSync(channelId)
const state = useGlobalChannelState(channelId);
const { submit, updateInput } = useChannelSync(channelId);
return (
<Composer.Frame>
<Composer.Input
value={state.input}
onChange={(text) => sync.updateInput(text)}
/>
<Composer.Input value={state.input} onChange={(text) => sync.updateInput(text)} />
<Composer.Submit onPress={() => sync.submit()} />
</Composer.Frame>
)
);
}
```
@@ -39,21 +36,17 @@ function ChannelProvider({
channelId,
children,
}: {
channelId: string
children: React.ReactNode
channelId: string;
children: React.ReactNode;
}) {
const { state, update, submit } = useGlobalChannel(channelId)
const inputRef = useRef(null)
const { state, update, submit } = useGlobalChannel(channelId);
const inputRef = useRef(null);
return (
<Composer.Provider
state={state}
actions={{ update, submit }}
meta={{ inputRef }}
>
<Composer.Provider state={state} actions={{ update, submit }} meta={{ inputRef }}>
{children}
</Composer.Provider>
)
);
}
// UI component only knows about the context interface
@@ -66,7 +59,7 @@ function ChannelComposer() {
<Composer.Submit />
</Composer.Footer>
</Composer.Frame>
)
);
}
// Usage
@@ -75,7 +68,7 @@ function Channel({ channelId }: { channelId: string }) {
<ChannelProvider channelId={channelId}>
<ChannelComposer />
</ChannelProvider>
)
);
}
```
@@ -84,28 +77,25 @@ function Channel({ channelId }: { channelId: string }) {
```tsx
// Local state for ephemeral forms
function ForwardMessageProvider({ children }) {
const [state, setState] = useState(initialState)
const forwardMessage = useForwardMessage()
const [state, setState] = useState(initialState);
const forwardMessage = useForwardMessage();
return (
<Composer.Provider
state={state}
actions={{ update: setState, submit: forwardMessage }}
>
<Composer.Provider state={state} actions={{ update: setState, submit: forwardMessage }}>
{children}
</Composer.Provider>
)
);
}
// Global synced state for channels
function ChannelProvider({ channelId, children }) {
const { state, update, submit } = useGlobalChannel(channelId)
const { state, update, submit } = useGlobalChannel(channelId);
return (
<Composer.Provider state={state} actions={{ update, submit }}>
{children}
</Composer.Provider>
)
);
}
```
@@ -15,15 +15,15 @@ or awkward refs.
```tsx
function ForwardMessageComposer() {
const [state, setState] = useState(initialState)
const forwardMessage = useForwardMessage()
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?
@@ -37,7 +37,7 @@ function ForwardMessageDialog() {
<ForwardButton /> {/* Needs to call submit */}
</DialogActions>
</Dialog>
)
);
}
```
@@ -45,20 +45,20 @@ function ForwardMessageDialog() {
```tsx
function ForwardMessageDialog() {
const [input, setInput] = useState('')
const [input, setInput] = useState('');
return (
<Dialog>
<ForwardMessageComposer onInputChange={setInput} />
<MessagePreview input={input} />
</Dialog>
)
);
}
function ForwardMessageComposer({ onInputChange }) {
const [state, setState] = useState(initialState)
const [state, setState] = useState(initialState);
useEffect(() => {
onInputChange(state.input) // Sync on every change 😬
}, [state.input])
onInputChange(state.input); // Sync on every change 😬
}, [state.input]);
}
```
@@ -66,13 +66,13 @@ function ForwardMessageComposer({ onInputChange }) {
```tsx
function ForwardMessageDialog() {
const stateRef = useRef(null)
const stateRef = useRef(null);
return (
<Dialog>
<ForwardMessageComposer stateRef={stateRef} />
<ForwardButton onPress={() => submit(stateRef.current)} />
</Dialog>
)
);
}
```
@@ -80,9 +80,9 @@ function ForwardMessageDialog() {
```tsx
function ForwardMessageProvider({ children }: { children: React.ReactNode }) {
const [state, setState] = useState(initialState)
const forwardMessage = useForwardMessage()
const inputRef = useRef(null)
const [state, setState] = useState(initialState);
const forwardMessage = useForwardMessage();
const inputRef = useRef(null);
return (
<Composer.Provider
@@ -92,7 +92,7 @@ function ForwardMessageProvider({ children }: { children: React.ReactNode }) {
>
{children}
</Composer.Provider>
)
);
}
function ForwardMessageDialog() {
@@ -107,12 +107,12 @@ function ForwardMessageDialog() {
</DialogActions>
</Dialog>
</ForwardMessageProvider>
)
);
}
function ForwardButton() {
const { actions } = use(Composer.Context)
return <Button onPress={actions.submit}>Forward</Button>
const { actions } = use(Composer.Context);
return <Button onPress={actions.submit}>Forward</Button>;
}
```