mirror of
https://github.com/langgenius/dify.git
synced 2026-05-10 05:56:31 +08:00
tweaks
This commit is contained in:
parent
bdd73d2846
commit
3fc652ee95
@ -5,49 +5,40 @@ description: React/TypeScript component style guide. Use when writing, refactori
|
||||
|
||||
# How To Write A Component
|
||||
|
||||
Do not copy existing code patterns blindly. Existing implementations are reference material, not automatic precedent; when existing code conflicts with these rules, follow this skill and adapt the approach instead of reproducing the violation.
|
||||
Use this as the decision guide for React/TypeScript component structure. Existing code is reference material, not automatic precedent; when it conflicts with these rules, adapt the approach instead of reproducing the violation.
|
||||
|
||||
## Reuse Existing Implementations
|
||||
## Core Defaults
|
||||
|
||||
- Before creating new UI, hooks, helpers, or styling patterns, search for and reuse existing base components, feature components, shared hooks, utilities, and established design styles. Add new implementations only when the existing ones cannot express the required behavior cleanly.
|
||||
- When styling components with Tailwind CSS, use Tailwind CSS v4.1+ syntax and the `tailwind-css-rules` skill: prefer v4 utility names, opacity modifiers, `gap`, `text-size/line-height`, `min-h-dvh`, and avoid deprecated utilities and `@apply`.
|
||||
- Search before adding UI, hooks, helpers, or styling patterns. Reuse existing base components, feature components, hooks, utilities, and design styles when they fit.
|
||||
- Group code by feature workflow, route, or ownership area: components, hooks, local types, query helpers, atoms, constants, and small utilities should live near the code that changes with them.
|
||||
- Promote code to shared only when multiple verticals need the same stable primitive. Otherwise keep it local and compose shared primitives inside the owning feature.
|
||||
- Use Tailwind CSS v4.1+ rules via the `tailwind-css-rules` skill. Prefer v4 utilities, `gap`, `text-size/line-height`, `min-h-dvh`, and avoid deprecated utilities and `@apply`.
|
||||
|
||||
## Feature Verticals And Local Ownership
|
||||
## Ownership
|
||||
|
||||
- Group code by what it does, not by whether it is a component, hook, type, utility, or constant.
|
||||
- Keep code that changes together in the same feature vertical: UI pieces, feature hooks, local types, query helpers, state, constants, and small utilities should stay near the workflow that owns them.
|
||||
- Choose vertical boundaries from route, page, product workflow, or team ownership. Split a nested vertical only when it is reused across surfaces or has an independent lifecycle.
|
||||
- Expose a small public surface for each vertical. Avoid deep-importing another vertical's private components, hooks, local helpers, or component-specific utilities.
|
||||
- Promote code to shared only when multiple verticals need the same stable primitive. Otherwise, keep it local and compose existing shared primitives inside the owning component or feature.
|
||||
- Put local state, queries, mutations, handlers, and derived UI data in the lowest component that uses them. Extract a purpose-built owner component only when the logic has no natural home.
|
||||
- Repeated TanStack query calls in sibling components are acceptable when each component independently consumes the data. Do not hoist a query only because it is duplicated; TanStack Query handles deduplication and cache sharing.
|
||||
- Hoist state, queries, or callbacks to a parent only when the parent consumes the data, coordinates shared loading/error/empty UI, needs one consistent snapshot, or owns a workflow spanning children.
|
||||
- Avoid prop drilling. One pass-through layer is acceptable; repeated forwarding means ownership should move down or into feature-scoped Jotai UI state. Keep server/cache state in query and API data flow.
|
||||
- Keep callbacks in a parent only for workflow coordination such as form submission, shared selection, batch behavior, or navigation. Otherwise let the child or row own its action.
|
||||
- Prefer uncontrolled DOM state and CSS variables before adding controlled props.
|
||||
|
||||
## Component Declaration And Exports
|
||||
## Components, Props, And Types
|
||||
|
||||
- Type component signatures directly; do not use `FC` or `React.FC`.
|
||||
- Prefer `function` for top-level components and module helpers. Use arrow functions for local callbacks, handlers, and lambda-style APIs.
|
||||
- Prefer named exports. Use default exports only where the framework requires them, such as Next.js route files.
|
||||
|
||||
## Props, Naming, And API Types
|
||||
|
||||
- Type simple one-off props inline. Use a named `Props` type only when reused, exported, complex, or clearer.
|
||||
- Use API-generated or API-returned types at component boundaries. Keep small UI conversion helpers beside the component that needs them.
|
||||
- Name values by their domain role and backend API contract, and keep that name stable across the call chain, especially IDs such as `appInstanceId`. Normalize framework or route params at the boundary instead of passing aliased names through components.
|
||||
- Name values by their domain role and backend API contract, and keep that name stable across the call chain, especially IDs like `appInstanceId`. Normalize framework or route params at the boundary.
|
||||
- Keep fallback and invariant checks at the lowest component that already handles that state; callers should pass raw values through instead of duplicating checks.
|
||||
|
||||
## State, Queries, And Callbacks
|
||||
|
||||
- Keep local state, queries, mutations, handlers, and derived UI data at the lowest component that uses them. Extract a purpose-built owner component when the logic has no natural home.
|
||||
- Avoid prop drilling. One pass-through layer is acceptable; repeated forwarding means ownership should move down or into shared UI state.
|
||||
- Keep callbacks in a parent only when it coordinates workflow, such as form submission, shared selection, batch behavior, or navigation. Otherwise, let the child or row own its action.
|
||||
- Use colocated, feature-scoped Jotai `atom`s for UI state shared across siblings or distant children. Keep server/cache state in query or API data flow.
|
||||
- Prefer uncontrolled components for DOM-owned state. Use CSS variables for visual customization before adding controlled props.
|
||||
|
||||
## Query And Mutation Contracts
|
||||
## Queries And Mutations
|
||||
|
||||
- Keep `web/contract/*` as the single source of truth for API shape; follow existing domain/router patterns and the `{ params, query?, body? }` input shape.
|
||||
- Consume queries directly with `useQuery(consoleQuery.xxx.queryOptions(...))` or `useQuery(marketplaceQuery.xxx.queryOptions(...))`.
|
||||
- Avoid pass-through hooks that only wrap `useQuery` and contract `queryOptions()`, such as `useAccessEnvironmentScope`.
|
||||
- Do not create new thin `web/service/use-*` wrappers for oRPC contract calls; inline legacy wrappers when they only rename one `queryOptions()` or `mutationOptions()` call.
|
||||
- Extract a small `queryOptions` helper only when repeated call-site options justify it; keep feature hooks for real orchestration, workflow state, or shared domain behavior.
|
||||
- Avoid pass-through hooks and thin `web/service/use-*` wrappers that only rename `queryOptions()` or `mutationOptions()`. Extract a small `queryOptions` helper only when repeated call-site options justify it.
|
||||
- Keep feature hooks for real orchestration, workflow state, or shared domain behavior.
|
||||
- For missing required query input, use `input: skipToken`; use `enabled` only for extra business gating after the input is valid.
|
||||
- Consume mutations directly with `useMutation(consoleQuery.xxx.mutationOptions(...))` or `useMutation(marketplaceQuery.xxx.mutationOptions(...))`; use oRPC clients as `mutationFn` only for custom flows.
|
||||
- Put shared cache behavior in `createTanstackQueryUtils(...experimental_defaults...)`; components may add UI feedback callbacks, but should not own shared invalidation rules.
|
||||
@ -56,23 +47,17 @@ Do not copy existing code patterns blindly. Existing implementations are referen
|
||||
|
||||
## Component Boundaries
|
||||
|
||||
- Prefer using the first level below a page or tab to organize and lay out independent page sections. This layer is optional when the page has only one main section or extracting it would only add a shallow wrapper.
|
||||
- Split deeper components by the data and state each layer actually needs. Each component should access only necessary data, and state should be pushed down to the lowest owner.
|
||||
- Use the first level below a page or tab to organize independent page sections when it adds real structure. This layer is layout/semantic first, not automatically the data owner.
|
||||
- Split deeper components by the data and state each layer actually needs. Each component should access only necessary data, and ownership should stay at the lowest consumer.
|
||||
- Keep cohesive forms, menu bodies, and one-off helpers local unless they need their own state, reuse, or semantic boundary.
|
||||
- Separate hidden secondary surfaces from the trigger's main flow. For dialogs, dropdowns, popovers, and similar branches, extract a small local component that owns the trigger, open state, and hidden content when that branch obscures the parent flow.
|
||||
- Preserve composability by separating behavior ownership from layout ownership. A dropdown action may own its trigger, open state, and menu content; the caller should own placement such as slots, offsets, and alignment.
|
||||
- Separate hidden secondary surfaces from the trigger's main flow. For dialogs, dropdowns, popovers, and similar branches, extract a small local component that owns the trigger, open state, and hidden content when it would obscure the parent flow.
|
||||
- Preserve composability by separating behavior ownership from layout ownership. A dropdown action may own its trigger, open state, and menu content; the caller owns placement such as slots, offsets, and alignment.
|
||||
- Avoid unnecessary DOM hierarchy. Do not add wrapper elements unless they provide layout, semantics, accessibility, state ownership, or integration with a library API; prefer fragments or styling an existing element when possible.
|
||||
- Avoid shallow wrappers and prop renaming. Call the original function directly unless the wrapper adds validation, orchestration, error handling, state ownership, or a real semantic boundary.
|
||||
- Avoid shallow wrappers and prop renaming unless the wrapper adds validation, orchestration, error handling, state ownership, or a real semantic boundary.
|
||||
|
||||
## Navigation
|
||||
## Navigation, Effects, And Performance
|
||||
|
||||
- Prefer `Link` for normal navigation. Use router APIs only for command-flow side effects such as mutation success, guarded redirects, or form submission.
|
||||
|
||||
## Effects
|
||||
|
||||
- Treat `useEffect` as a last resort. First try deriving values during render, moving event-driven work into handlers, or using existing hooks/APIs for persistence, subscriptions, media queries, timers, and DOM sync.
|
||||
- Do not use `useEffect` directly in components. If unavoidable, encapsulate it in a purpose-built hook so the component consumes a declarative API.
|
||||
|
||||
## Performance
|
||||
|
||||
- Avoid `memo`, `useMemo`, and `useCallback` unless there is a clear performance reason.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user